Phone 设置项中可以模拟来电和短信:From:选择或输入电话号码;CALL DEVICE:点击来电;HOLD CALL:点击保持通话;END CALL:点击结束通话;SMS message:输入模拟消息的正文内容;SEND MESSAGE:点击发送短信。
模拟10086来电;gsm call 10086模拟接听来电;gsm accept 10086模拟挂断来电;gsm cancel 10086下面是完整会话示例。pt@Win10-Panda:~$ telnet localhost 5554Trying 127.0.0.1...Connected to localhost.Escape character is '^]'.Android Console: Authentication requiredAndroid Console: type 'auth <auth_token>' to authenticateAndroid Console: you can find your <auth_token> in'C:\Users\panda\.emulator_console_auth_token'OKauth KeGgPVHDN7YifXfZAndroid Console: type 'help' for a list of commandsOKgsm call 10086OKgsm accept 10086OKgsm cancel 10086OK
命令说明gsm {call\accept\cancel\busy} phonenumber模拟电话的呼入、接听、挂断、繁忙。gsm {data\voice} statedata state 命令用于更改 GPRS 数据连接的状态,data voice state 则用于更改 GPRS 语音连接的状态。gsm hold将通话状态更改为 hold。只有在当前状态为 active 或 waiting 时,才能将通话状态更改为 hold。gsm list列出所有呼入电话和外拨电话及其通话状态。gsm status报告当前的 GSM 语音/数据连接状态。相关的值就是前述 voice 和 data 命令的值。
uni-app 的 API 与微信小程序 API 基本一致。掌握微信小程序 API 对后面的开发很有帮助。微信小程序 API 文档:https://developers.weixin.qq.com/miniprogram/dev/api/
当程序中需要多层条件判断的时候,会使用嵌套的分支结构,用来根据不同的条件组合,来跳转执行不同的处理语句。这在程序中是非常常见的,分支嵌套结构就如同我们日常生活中的很多计划内容。比如,我们有这样一个场景:如果今天天气好,我们就去郊游,如果天气不好我们待在家里。打开天气预报发现今天的天气不错,诶,这个时候我们级就可以去郊游。换算到程序中也就是相当于满足了“天气好”这样一个条件,执行了“去郊游”这样的语句。我们去郊游的话肯定要带些娱乐的东西,比如说扑克牌或者围棋等等,那么到底带什么呢?我们决定如果去郊游的人数大于 2 人就带扑克牌,如果只有 2 人,那么只能带围棋了。也就是说满足“郊游人数大于 2 人”的条件就会执行“带扑克牌”语句,如果不满足的话执行“带围棋”语句。
事件日志中包含屏幕电源状态,其中 0 表示屏幕关闭,1 表示屏幕打开,2 表示已锁屏。日志范例如下:grep screen_toggled bugreport-2015-10-18-16-52-22.txt10-18 15:05:04.383 992 992 I screen_toggled: 110-18 15:05:07.010 992 992 I screen_toggled: 010-18 15:23:15.063 992 992 I screen_toggled: 110-18 15:23:25.684 992 992 I screen_toggled: 010-18 15:36:31.623 992 992 I screen_toggled: 110-18 15:36:37.660 3283 3283 I screen_toggled: 2错误报告中还包含关于唤醒锁的统计信息,唤醒锁是应用开发者采用的一种机制,用于表明其应用需要设备保持开启状态。唤醒锁总时长统计信息仅跟踪唤醒锁实际负责使设备保持唤醒状态的时间,不包括屏幕处于开启状态的时间。此外,如果同时持有多个唤醒锁,系统会在它们之间分配唤醒锁时长。
可以看到,乐观锁虽然有缺陷,它会使更新失败,因此必须重复获取数据然后重试,但是它保证了数据的正确性和完整性。在读多写少的场景下,乐观锁不会出现太多的重试,当然如果出现了很多重试,证明场景已经可能不是读多写少了,可以尝试换方案了。乐观锁的实现也颇为简单,不需要任何第三方依赖,你完全可以自己直接实现,不过仍然有一些第三方框架提供了开箱即用的乐观锁,你可以根据自己的使用语言和生态去查找相应的乐观锁框架。
为了更好地理解悲观锁与乐观锁,我们通过设置一个简单的示例场景来进行分析。并且我们采用悲观锁 synchronized 和乐观锁 Atomic 操作进行分别实现。Atomic 操作类,指的是 java.util.concurrent.atomic 包下,一系列以 Atomic 开头的包装类。例如 AtomicBoolean,AtomicInteger,AtomicLong。它们分别用于 Boolean,Integer,Long 类型的原子性操作。Atomic 操作的底层实现正是利用的 CAS 机制,而 CAS 机制即乐观锁。场景设计:创建两个线程,创建方式可自选;定义一个全局共享的 static int 变量 count,初始值为 0;两个线程同时操作 count,每次操作 count 加 1;每个线程做 100 次 count 的增加操作。结果预期:最终 count 的值应该为 200。悲观锁 synchronized 实现:public class DemoTest extends Thread{ private static int count = 0; //定义count = 0 public static void main(String[] args) { for (int i = 0; i < 2; i++) { //通过for循环创建两个线程 new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(10); } catch (Exception e) { e.printStackTrace(); } //每个线程让count自增100次 for (int i = 0; i < 100; i++) { synchronized (DemoTest.class){ count++; } } } }). start(); } try{ Thread.sleep(2000); }catch (Exception e){ e.printStackTrace(); } System.out.println(count); }}结果验证:200乐观锁 Atomic 操作实现:public class DemoTest extends Thread{ //Atomic 操作,引入AtomicInteger。这是实现乐观锁的关键所在。 private static AtomicInteger count = new AtomicInteger(0); public static void main(String[] args) { for (int i = 0; i < 2; i++) { new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(10); } catch (Exception e) { e.printStackTrace(); } //每个线程让count自增100次 for (int i = 0; i < 100; i++) { count.incrementAndGet(); } } }). start(); } try{ Thread.sleep(2000); }catch (Exception e){ e.printStackTrace(); } System.out.println(count); }}结果验证:200代码解读:此处主要关注两个点,第一个是 count 的创建,是通过 AtomicInteger 进行的实例化,这是使用 Atomic 的操作的入口,也是使用 CAS 乐观锁的一个标志。第二个是需要关注 count 的增加 1 调用是 AtomicInteger 中 的 incrementAndGet 方法,该方法是原子性操作,遵循 CAS 原理。
注册微信小程序账号,获取到 AppID,我们后面配置的时候会用到。在 HBuilderX 工具栏,点击发行,选择小程序-微信。输入小程序名称和 AppID,单击发行就可以了。这样我们就会获得一个微信小程序的打包文件,接下来我们来发布微信小程序项目,打开微信小程序开发者工具,导入刚刚生成的微信小程序项目的打包文件,在微信小程序开发者工具中先测试一下,项目运行是否正常,项目测试没有问题后,点击右上角>>按钮,上传代码就可以发布微信小程序了,最后等待微信团队审核通过,别人就可以在线上访问到你的项目了。
数据转换成电信号还是比较简单的,将字符根据指定的编码转换成唯一的二进制编码,然后将二进制的 01 映射成高低电平就可以传输出去了。
通过传输层,建立了数据的传输通道。不同应用有自己不同的会话标识,所以传输过来的数据根据会话标识能够知道跟电脑的哪个应用在通信的。
① 定义:第三范式(Third Normal Form)要求数据库表中的每个字段和主键都直接相关,不能间接相关。② 案例解释:还是以第一范式中的user_info表作为案例,如果要存储每个用户的省份和省会城市,我们可能会设计出下面这样一张表:用户编号(user_id)姓名(username)年龄(age)座机电话(fixed_phone)手机电话(cell_phone)省份(province)省会城市(city)1小明201008618010002000北京市北京市2小红211008718010002001黑龙江省哈尔滨市3小王221008818010002002贵州省贵阳市我们将用户编号(user_id)作为主键,则姓名、年龄、座机电话、手机电话都和"用户"这个主体强相关,和主键直接相关,而省份和省会城市则和"用户"这个主体是弱相关,和主键间接相关,并且存在依赖关系:用户编号 -> 姓名,姓名 -> 省份,省份 -> 省会城市,这样构建了用户编号 -> 省会城市的间接传递关系,这种关系会导致数据冗余,而且在执行删除/修改/增加操作的时候,会产生异常情况:删除所有"贵州省"下的用户信息(即user_id = 3的记录),"贵州省"和"贵阳市"的信息也被删除了(显然不合理,因为省份这个定义和省份下的人员记录并没有关系)。所以我们需要将user_info表拆分,我们通过省份构建数据关系,优化后的用户(user_info)表如下:用户编号(user_id)姓名(username)年龄(age)座机电话(fixed_phone)手机电话(cell_phone)省份(province)1小明201008618010002000北京市2小红211008718010002001黑龙江省3小王221008818010002002贵州省独立拆分出的省份(province)表如下:省份(province)省会城市(city)北京市北京市黑龙江省哈尔滨市贵州省贵阳市③ 范式优点:提高了表的独立性,降低数据存储冗余。
简单的来说 CAS 适用于写比较少的情况下(多读场景,冲突一般较少),synchronized 适用于写比较多的情况下(多写场景,冲突一般较多)。对于资源竞争较少(线程冲突较轻)的情况,使用 synchronized 同步锁进行线程阻塞和唤醒切换以及用户态内核态间的切换操作额外浪费消耗 CPU 资源;而 CAS 基于硬件实现,不需要进入内核,不需要切换线程,操作自旋几率较少,因此可以获得更高的性能;对于资源竞争严重(线程冲突严重)的情况,CAS 自旋的概率会比较大,从而浪费更多的 CPU 资源,效率低于 synchronized。总结:乐观锁适用于写比较少的情况下(多读场景),即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。但如果是多写的情况,一般会经常产生冲突,这就会导致上层应用会不断地进行 retry,这样反倒是降低了性能,所以一般多写的场景下用悲观锁就比较合适。
IP 地址,物理地址网站后台访问地址,密码信息家庭成员信息,电话信息生日(很多人的秘密是生日日期)公司信息,同事信息
乐观是一种积极的解决问题的态度。所谓乐观锁认为系统中的事务并发更新不会很频繁,即使冲突了也没事,大不了重新再来一次。基本思想:每次提交一个事务更新时,查看要修改的数据从上次读取以后有没有被其它事务修改过,如果修改过,那么更新就会失败。实现方案:在实体中增加一个版本控制字段,每次事务更新后就将版本 (Version) 字段的值加 1。Tips: 乐观锁本质就是版本控制管理的实现,记录的每一次更新操作都会以版本递增的方式进行记录。一个事务在更新之前,先获取记录的当前版本号,更新时,如果版本还是最新的则可以更新,否则说明有事务比你先更新,则需要放弃。或者重新查询到最新版本信息后再更新。所以,在乐观锁的实现中,冲突是常态。实现过程:在学生实体类中添加新属性,用来记录每次更新的版本号。public class Student implements Serializable {//省略…… @Versionprivate Long version;//省略…… }stu = (Student) session.get(Student.class, new Integer(1));System.out.println("当前版本号:"+stu.getVersion);//模拟延迟,如果在这个时间内有其它事务进行了更新操作,此事务的更新不会成功Thread.sleep(30000);stu.setStuName("Hibernate");transaction.commit();好了,悲观也好,乐观也好,只是一种解决问题的态度。对于这两种态度,咱们要总结一下。乐观锁:优势:性能好,并发性高。缺点:用户体验不好,可能会出现高高兴兴去更新,却告知已经有人捷足先登了。悲观锁:优势:锁住记录为我所用,没修改完成之前,其他事务只能瞪眼瞧着,时间虽然延迟,至少心里有底。缺点:并发性不好,性能不高。Hibernate 的其它性能优化:随时使用 Session.clear()及时清除 Session 缓存区的内容;1+N 问题 ( 一条 SQL 语句能解决的问题用了很多条 SQL 语句来实现) ;使用 Criteria 查询可以解决这个问题;Lazy 加载:需要时,使用 get() 方法发出 SQL 语句。使用类似于 from Student s left join s.classRoom c 的关联查询语句。缓存使用:在对象更新、删除、添加相对于查询要少得多时, 二级缓存的应用将不怕 n+1 问题,因为即使第一次查询很慢,之后直接缓存命中也是很快的,刚好又利用了 n+1。
命令说明power display显示电池和充电器状态。power ac {on\off}将交流电充电状态设为 on 或 off。power status {unknown\charging\discharging\not-charging\full}按照说明更改电池状态。power present {true\false}设置电池存在状态。power health {unknown\good\overheat\dead\overvoltage\failure}设置电池运行状况。power capacity percent将电池剩余电量状态设为 0 到 100 之间的百分比。
首先我们需要一个策略接口,不同策略实现该接口。再搭配一个策略工厂。客户端代码只需要根据用户的出行方式,让工厂返回具体实现即可,由具体的实现来提供算法计算。以工厂模式实现的电子地图代码如下。TravelStrategy接口代码:public interface TravelStrategy { int calculateMinCost();}TravelStrategy接口的实现代码:public class SelfDrivingStrategy implements TravelStrategy { @Override public int calculateMinCost() { return 30; }}TravelStrategyFactory代码:public class TravelStrategyFactory { public TravelStrategy createTravelStrategy(String travelWay) { if ("selfDriving".equals(travelWay)) { return new SelfDrivingStrategy(); } if ("bicycle".equals(travelWay)) { return new BicycleStrategy(); } else { return new PublicTransportStrategy(); } }}TravelService对外提供计算方法,通过工厂生成所需要的 strategy。代码如下:public class TravelService { private TravelStrategyFactory travelStrategyFactory = new TravelStrategyFactory(); public int calculateMinCost(String travelWay) { TravelStrategy travelStrategy = travelStrategyFactory.createTravelStrategy(travelWay); return travelStrategy.calculateMinCost(); }}代码结构和我们上一节讲解的音乐推荐器几乎一模一样。看似也很好地解决了我们的设计问题。接下来我们看看如何用策略模式解决这个问题,然后我们再对两种模式做对比。
定义:乐观锁是相对悲观锁来说的,它认为数据在一般情况下不会造成冲突,所以在访问记录前不会加排它锁,而是在进行数据提交更新的时候,才会正式对数据冲突与否进行检测。乐观锁的实现:依旧拿数据库的锁进行比较介绍,乐观锁并不会使用数据库提供的锁机制, 一般在表中添加 version 宇段或者使用业务状态来实现。 乐观锁直到提交时才锁定,所以不会产生任何死锁。Java 中的乐观锁:我们之前所学习的 CAS 原理即是乐观锁技术,当多个线程尝试使用 CAS 同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。Tips:我们这里所说的对于乐观锁,当多个线程尝试使用 CAS 同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败。注意失败两字,失败意味着有操作,而悲观锁是等待,意味着不能同时操作。
业务场景描述有这样一个真实的业务场景:在某大厂的订单与支付模块,当有用户下了订单之后,需要在支付模块进行支付,支付动作完成之后,支付模块会将支付完成的结果返回给订单模块来通知用户,该订单是否支付成功,即商品是否已经成功购买了。在微服务分布式架构模式下,上述业务场景中出现了一种异常现象:当用户下了订单之后,在支付模块进行支付时,系统一直没有响应,无论是否成功支付,用户都收不到任何通知信息。程序员在排查对应的业务实现代码时,证实了业务实现代码没有问题,这就导致无法定位问题所在。最终经过几名同事一起排查,发现是订单模块与支付模块之间进行数据传输时,支付模块收到了订单模块传递过来的数据,但是由于服务器高压工作,导致支付模块始终无法处理该支付请求,这就导致系统一直没有响应。问题原因分析在解决问题之前,我们首先来分析一下这种问题产生的原因。在前面我们介绍什么是 Hystrix 资源隔离小节中,我为大家阐述了在我们的 Web 项目中,进程与线程之间的关系。我们知道,在一般情况下,一个 Web 项目中只有一个工作线程来负责处理用户调用的请求和服务,当该工作线程所负责的请求处理缓慢时,该线程就会一直处理当前的请求,导致后续请求只能等待处理,这就是我们说的雪崩现象。雪崩效应产生原理在微服务分布式架构模式下,由于我们没有对线程进行处理,至此在处理所有业务请求时,扔是只有一个工作线程,这就导致上述业务场出现了我们所说的雪崩现象,不过还好,这种雪崩现象比较轻微,只影响到了一个业务模块。很多时候,当我们的项目架构演变为基于微服务的分布式架构时,服务器也需要同步进行更新,有很多企业为了节约成本,则只更新很少数量的服务器,或者压根就不更新服务器,这就导致经常会出现由于服务器高压工作而出现的请求处理缓慢,或请求无法继续处理的情况。
AtomicInteger 经常用于多线程操作同一个整型变量时,简化对此变量的线程安全控制的场合。当在研发过程中遇到这些场景时,就可以考虑直接使用 AtomicInteger 工具类辅助实现,完全可以放弃使用 synchronized 关键字做同步控制。下面我们用 AtomicInteger 工具实现电影院某场次电影票销售的例子。
乐观锁不全依赖于数据库,一般情况下我们都是在代码层面上来完成它的,主流的设计思路是这样的:我们在数据表中添加一个字段version,version 代表版本号,字段类型为整型。当我们获取数据时,假设得到它的version字段为n,执行完其它操作对该数据进行更新时,会执行UPDATE ... SET version=n+1 WHERE version=n。如果在更新时,数据已经被别人更新过了,那么该数据的version字段已经不是n了,那么此时修改就会失败,反之修改就会成功。可以看到,乐观锁就像它的名称一样乐观,适合数据读多写少的场景,因为实际上并没锁住数据,所以性能十分可观;而悲观锁则与之相反,适合写多读少的场景,盲目的排他性一定程度上会大幅影响性能。
微前端 尚处在发展时期,其核心概念和 微服务 相似。现阶段较为常用的微前端框架为 single-spa 和 qiankun,后者是基于前者实现的。该技术能做到 技术栈无关,即一个应用,能由多个不同技术的子应用构成,同时做到子应用的相互隔离,这里的隔离就可以选择采用 Web Components 实现。
悲观锁:总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样其他线程想拿这个数据就会阻塞直到它拿到锁(共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程)。乐观锁:总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制和 CAS 算法实现。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于 write_condition 机制,其实都是提供的乐观锁。
会话工厂的核心作用:建立和数据库系统的连接,创建会话对象。创建时需要依赖 Configuration 组件解析出来的信息。Hiberante 的会话工厂的源代码实现比较复杂,代码具有良好的结构性,会衍生出很多辅助性组件。因为只是说明问题,本文中的工厂仅仅用来创建会话对象,不考虑性能、缓存、事务等各方面问题,也不刻意讲究代码的结构性。简易框架中的会话工厂的代码简单但具有说明力:public class SessionFactory { private Map<String, String> pros; private List<String> mappings; private ThreadLocal<Session> threadLocal; public SessionFactory(Map<String, String> pros, List<String> mappings) { this.pros = pros; this.mappings = mappings; this.threadLocal = new ThreadLocal<Session>(); } public Connection getConn() { String driverClass = this.pros.get("connection.driver_class"); String url = this.pros.get("connection.url"); String userName = this.pros.get("connection.username"); String password = this.pros.get("connection.password"); try { Class.forName(driverClass); return DriverManager.getConnection(url, userName, password); } catch (ClassNotFoundException | SQLException e) { e.printStackTrace(); } return null; } public Session openSession() { return new Session(this.getConn(), this.mappings); } public Session getCurrentSession() { Session session = this.threadLocal.get(); if (session == null) { session = this.openSession(); this.threadLocal.set(session); } return session; }}会话工厂类中有 2 个创建 Session 的方法,一个是普通的创建方法,另一个是从线程上下文中检查是否已经存在 Session 对象。
移动端大部分指的是手机,大家目前的生活基本已经离不开手机了。但是手机的迭代速度可不同于电脑,相信来到慕课网的小伙伴们都是想学习编程的,想学习编程的话基本上都得有一台电脑。无论是笔记本电脑还是台式电脑,大家可以发现其寿命要远高于手机。通常情况下只要你不是处于一种极端环境下使用电脑的话一般都可以用很多年。这也就是为什么做 PC 网站的程序员有一部分还要去兼容IE的原因, WindowsXP 至今还有人在使用,已经停止维护了的 win7 目前市场份额仍然高居 25% 左右。只要保养得当台式机通常都可以用七年以上,七年之痒不再是难题,笔记本的话也至少可以使用 3 年。
我们只需要通过配置文件指定数据源信息, Spring Boot 就可以识别配置,并加载到数据源组件中。 JdbcTemplate 也可以自动识别该数据源,从而实现对数据库的操作。配置文件信息如下:实例:# 配置数据库驱动spring.datasource.driver-class-name=com.mysql.jdbc.Driver# 配置数据库urlspring.datasource.url=jdbc:mysql://127.0.0.1:3306/shop?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8# 配置数据库用户名spring.datasource.username=root# 配置数据库密码spring.datasource.password=123456需要注意的是,我们在 URL 配置中指定了编码方式,这样可以防止出现数据库中文乱码情况。同时指定了时区为北京时间所在的东八区(GMT%2B8),避免因时区问题导致错误。此时再次启动 Spring Boot 应用,正常运行,说明我们的数据源配置生效了。
Battery 设置中共用4项参数可以调节,模拟设备的电池属性,了解应用在不同条件下的运行情况:Charge level:设置当前电量值,百分比;Charger connection:设置充电器连接状态,是否连接充电器;battery health:设置电池监控状态,正常或者损坏等;Battery status:设置电池状态,正在充电或者已经充满等。
豆瓣网 (douban) 是一个社区网站,提供关于书籍、电影、音乐等作品的信息,还提供书影音推荐、线下同城活动、小组话题交流等多种服务功能。豆瓣网已经达拥有 300 万注册用户,访问量每天则超过两千万,是一个应用 Python 开发的非常成功的 Web 2.0 站点。豆瓣网的后端采用了 Quixote 框架,Quixote 框架是一个使用 Python 开发的轻量级 Web 框架。
Flask 是一个 Python 实现的 Web 开发微框架,但是这个“微”并不代表着 Flask 功能比较简陋、有所欠缺。微框架中的 “微” 意味着:Flask 旨在保持核心简单而易于扩展;Flask 不会替用户做出太多决策,比如使用何种数据库;Flask 的选项(比如使用何种模板引擎) 通常有多个,用户很容易替换。默认情况下,Flask 不包含数据库抽象层、模板引擎、身份认证或其它任何已有多种库可以胜任的功能,如下图所示。然而,Flask 支持用扩展来给应用添加这些功能,应用程序可以很方便的集成这些扩展。众多的扩展提供了数据库集成、表单验证、上传处理、各种各样的开放认证技术等功能。
实现思路分析针对上述场景,我们需要将所有拆分出来的微服务都要集成到 Hystrix 的微服务监控平台中去,但是我们应该怎么集成呢?Hystrix 针对不同场景下,提供了不同的微服务监控方式:针对单体架构或只需要对一个服务实例进行监控的业务场景,我们只需要像我在介绍微服务监控平台时那样,进行配置即可;针对分布式的项目结构,或同时需要监控多个微服务的场景,Hystrix 提供了一个名为 Turbine 的多服务器微服务监控组件。我们只需要将 Turbine 引入到我们的项目中去,即可实现分布式架构下的多微服务监控了。实操要想在项目中引入 Turbine ,我们首先需要将 Turbine 的依赖引入到项目中:<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-turbine</artifactId></dependency>在引入了 Turbine 依赖之后,我们需要对 Turbine 进行一些简单的配置,在 yml 配置文件中:turbine: aggregator: clusterConfig: default appConfig: service-one,service-two,service-three clusterNameExpression: "'default'"代码解释第 3 行,我们通过 clusterConfig 属性来配置 turbine 采用的集群策略,一般我们配置 default 即可。第 4 行,我们通过 appConfig 属性来配置需要继承到 turbine 中的微服务,即需要同时刻进行监控的微服务,多个微服务使用英文状态下的逗号隔开。第 5 行,我们通过 clusterNameExpression 属性来配置集群的名称表达式,一般也是采用 default 配置即可。yml 配置文件配置完成后,我们需要在项目的启动类中添加 turbine 的注解:@SpringBootApplication@EnableHystrix@EnableHystrixDashboard@EnableTurbinepublic class SaleApplication { public static void main(String[] args) { SpringApplication.run(SaleApplication.class, args); }代码解释第 4 行,我们添加了 @EnableTurbine 注解,来在项目中开启 turbine 服务,以支持同时监控多个微服务。在配置完了上述两部分后,我们的 Hystrix Turbine 组件就引入到了我们的项目中,我们就可以对多个微服务进行监控了。配置完成后,运行我们的项目,在浏览器地址栏中输入我们的监控地址:http://localhost:port/turbine.stream我们即可在浏览器中看到多个微服务已经被监控了,如下图所示:Hystrix Dashboard 多微服务同时监控从上图可知,这里我们对两个微服务进行了监控,其图中的各项监控参数和我们在之前介绍 Hystrix 微服务监控平台中的参数一样,这里就不再赘述了。Tips: 1. 在 appConfig 属性配置中,属性的值为每个需要监控的微服务的 serviceId 名称,同学们不要配置错了。 2. 各位在引入 Turbine 的依赖时,注意不要引错依赖了,如果大家引入了 aitifactId 为 spring-cloud-starter-turbine 的依赖,我们是无法使用 Turbine 的,因为这个依赖并不是 Spring Cloud 微服务组件中所提供的 Turbine 服务,而是单独的 Turbine 服务,我们无法在微服务项目中使用。 3. 各位同学在访问 Turbine 多微服务监控平台时,注意访问地址发生了变化,大家使用之前的微服务监控平台访问地址时访问不到的,这点需要同学们注意。