微服务架构的特点就是:“一解释就懂,一问就不知,一讨论就吵架”
服务熔断:一般是指软件系统中,由于某些原因使得服务出现了过载现象,为防止造成整个系统故障,从而采用的一种保护措施,所以很多地方把熔断亦称为过载保护。
服务降级:就是整体资源快不够了,忍痛将某些服务先关掉,待渡过难关,再开启回来。
两者类似性的:
- 目的很一致,都是从可用性可靠性着想,为防止系统的整体缓慢甚至崩溃,采用的技术手段;
- 最终表现类似,对于两者来说,最终让用户体验到的是某些功能暂时不可达或不可用;
- 粒度一般都是服务级别,当然,业界也有不少更细粒度的做法,比如做到数据持久层(允许查询,不允许增删改);
- 自治性要求很高,熔断模式一般都是服务基于策略的自动触发,降级虽说可人工干预,但在微服务架构下,完全靠人显然不可能,开关预置、配置中心都是必要手段;
两者的区别:
- 触发原因不太一样,服务熔断一般是某个服务(下游服务)故障引起,而服务降级一般是从整体负荷考虑;
- 管理目标的层次不太一样,熔断其实是一个框架级的处理,每个微服务都需要(无层级之分),而降级一般需要对业务有层级之分(比如降级一般是从最外围服务开始)
- 实现方式不太一样;
工作流程
1.创建 HystrixCommand 或 HystrixObservableCommand 对象
首先,创建一个 HystrixCommand 或 HystrixObservableCommand 对象,用来表示对依赖服务的操作请求,同时传递所有需要的参数。从其命名中我们就能知道它采用了“命令模式” 来实现服务调用操作的封装。而这两个 Command 对象分别针对不同的应用场景。
- HystrixCommand :用在依赖的服务返回单个操作结果的时候。
- HystrixObservableCommand :用在依赖的服务返回多个操作结果的时候。
命令模式,将来自客户端的请求封装成一个对象,从而让你可以使用不同的请求对客户端进行参数化。它可以被用于实现“行为请求者” 与 “行为实现者” 的解耦,以便使两者可以适应变化。
2. 命令执行
命令执行方式一共有4种,而 Hystrix 在执行时会根据创建的Command对象以及具体的情况来选择一种执行。其中 HystrixCommand 实现了下面两个执行方式。
- execute():同步执行,从依赖的服务返回一个单一的结果对象,或是在发生错误的时候抛出异常。
- queue():异步执行,直接返回一个 Future 对象,其中包含了服务执行结束时要返回的单一结果对象。
而 HystrixObservableCommand 实现了另外两种执行方式。
- observe():返回 Observable 对象,它代表了操作的多个结果,它是一个 HotObservable。
- toObservable():同样返回 Observable 对象,也代表了操作的多个结果,但它返回的是一个 Cold Observable。
在 Hystrix 的底层实现中大量使用了 RxJava ,为了更容易的理解后续内容,在这里对 RxJava 的观察者-订阅者模式做一个简单的入门介绍。
上面提到的 Observable 对象就是 RxJava 中的核心内容之一,可以理解为 “事件源” 或者 “被观察者”,与其对应的 Subscriber 对象,可以理解为 “订阅者” 或者 “观察者”。这两个对象是 RxJava 响应式编程的重要组成部分。
- Observable 用来向订阅者 Subscriber 对象发布事件,Subscriber 对象则在接收到事件后对其进行处理,而在这里所指的事件通常就是对依赖服务的调用。
- 一个 Observable 可以发出多个事件,知道结束或者发生异常。
- Observable 对象每发出一个事件,就会调用对应观察者 Subscriber 对象的 onNext() 方法。
- 每一个 Observable 的执行,最后一定会通过调用 Subscriber.onCompleted() 或者 Subscriber.onError() 来结束该事件的操作流。
3. 结果是否被缓存
若当前命令的请求缓存功能是被启用的,并且该命令缓存命中,那么缓存的结果会立即以 Observable 对象的形式返回。
4. 断路器是否打开
在命令结果没有缓存命中的时候,Hystrix 在执行命令前需要检查断路器是否为打开状态。
- 打开:Hystrix不执行命令,转到 fallback 处理逻辑(对应下面第8步)。
- 关闭:Hystrix 跳到第5步,检查是否有可用资源来执行命令。
5. 线程池 / 请求队列 / 信息量是否占满
如果与命令相关的线程池 / 请求队列 / 信息量已经占满,那么 Hystrix 不会执行命令,跳转到 fallback 处理逻辑(对应下面第8步)。
注意:此处的线程池并非容器的线程池,而是每个依赖服务的专有线程池。Hystrix 为了保证不会因为某个依赖服务的问题影响到其他依赖服务而采用了 “舱壁模式” 来隔离每个依赖的服务。
6. HystrixObservableCommand.construct() 或 HystrixCommand.run()
Hystrix 会根据编写的方法来决定采取什么样的方式去请求依赖服务。
- HystrixCommand.run() :返回一个单一的结果,或者抛出异常。
- HystrixObservableCommand.construct():返回一个 Observable 对象来发射多个结果,或通过 onError 发送错误通知。
如果 run() 或 construct() 方法的执行时间超过了命令设置的超时阈值,当前处理线程会抛出 TimeoutException。这种情况下,也会跳转到 fallback 处理逻辑(第8步)。
7. 计算断路器的健康度
Hystrix 会将 “成功”、“失败”、“拒绝”、“超时” 等信息报告给断路器,而断路器会维护一组计数器来统计这些数据。
断路器会使用这些统计数据来决定是否要将断路器打开,来对某个依赖服务的请求进行 “熔断 / 短路”,直到恢复期结束。
8. fallback 处理
当命令执行失败的时候,Hystrix 会进入 fallback 尝试回退处理,我们通常也称为 “服务降级”。下面就是能够引发服务降级处理的几种情况:
- 第4步,当前命令处于 “熔断 / 短路” 状态,断路器是打开的时候。
- 第5步,当前命令的线程池、请求队列或者信号量被占满的时候。
- 第6步,HystrixObservableCommand.construct() 或者 HystrixCommand.run() 抛出异常的时候。
9、返回成功的响应