思考的过程每每比直接获得结论更加剧要html
在分布式环境下,不可避免的就是服务之间的调用。A 调 B,B 可能会失败,若是此时 B 服务挂掉,那么会致使服务 A 由于服务 B 的失败而失败。从而致使 客户端认为 A 也是失败的。 简单说就是,牵一发而动全身java
这也是,咱们须要熔断器的缘由。咱们须要有保护服务调用的组件。当服务 B 挂掉时,服务 B 须要可以快速失败。git
思考一下,什么是隔离?
我对他的理解,大概就是,服务 A 调用服务 B,服务 C。不能由于调用服务 B 出现问题,而致使调用服务 C 也出现问题。也就是,服务B、服务C 的调用应该放在不一样的环境下。github
常见的资源隔离,有线程池隔离,信号量隔离算法
线程池隔离 | 信号量隔离 | |
---|---|---|
线程 | 请求线程和调用 provider 不是同一个线程 | 请求线程和调用 provider 是同一个线程 |
开销 | 排队、调度、上下文开销等 | 无线程切换,开销低 |
异步 | 支持 | 不支持 |
并发支持 | 支持(线程池大小) | 支持(信号量上限) |
传递 Header | 没法传递 Http Header | 能够传递 Http Header |
当 provider 提供的服务不可用,或出现异常时,应该有可供回调的降级方法。编程
当 provider 被大量调用时,为了保护链路,须要作限流。那么其实核心的问题是,如何进行统计。设计模式
每一个 provider 的调用状况须要统计。这样能够更好的监控到 provider 提供的服务的状况。统计的算法,应该是基于滑动窗口进行统计。tomcat
由于有对每一个 provider 调用状况统计,在调用以前,失败次数达到某个阈值时,能够认为该 provider 已是有问题的,能够直接快速失败,以防止服务雪崩状况。并发
熔断机制是针对每一个 provider 的。可是,有可能服务系统已经要达到极限了,不能再接收任何请求了,那么此时,出于系统的保护,也应该快速失败。异步
做为一个组件,支持扩展那是必须。
常见的扩展方式:(其实就是面向接口编程,在某些执行流程中,暴露出部分接口。剩下的就看你怎么封装了)
public interface SayListener { String say(); } public class App { List<SayListener> sayListener = new ArrayList<>(); public void doSomething() { // ... sayAction(); // 触发了 say 的动做 if (null != sayListener && sayListener.size() > 0) { sayListener.forEach(listener -> { listener.say(); }); } // ... } public void sayAction() {} public void registerSayListener(SayListener listener) { sayListener.add(listener); } }
sentinel | hystrix | resilience4j | |
---|---|---|---|
隔离策略 | 信号量隔离(并发线程数限流) | 线程池隔离/信号量隔离 | 信号量隔离 |
熔断降级策略 | 基于响应时间、异常比率、异常数 | 基于异常比率 | 基于异常比率、响应时间 |
实时统计实现 | 滑动窗口(LeapArray) | 滑动窗口(RxJava) | Ring Bit Buffer |
动态规则配置 | 支持多种数据源 | 支持多种数据源 | 有限支持 |
扩展性 | 多个扩展点 | 插件形式 | 接口形式 |
基于注解的支持 | 支持 | 支持 | 支持 |
限流 | 基于 QPS, 支持基于调用关系的限流 | 基于线程池个数有限支持 | Rate Limiter |
系统自适应保护 | 支持 | 不支持 | 不支持 |
控制台 | 丰富 | 简单 | 不提供控制台,可对接其余监控系统 |
hystrix wiki 介绍实在太全了,这里就不必在介绍了,主要是从我的的角度去思考若是让本身也实现一个断路器中间件,你会怎么作?(鉴于 hystrix 中止维护,就没看源码了,以及 sentinel 的活跃,重心会放在 sentinel 上)
在使用 hystrix 须要注意的几个地方。
设置 hystrix 超时时间时,须要大于等于 ribbon 的超时时间。 例如 ribbon 的配置以下
ReadTimeout=1000 ConnectTimeout=1000 MaxAutoRetries=1 MaxAutoRetriesNextServer=1
那么 hystrix 的超时时间公式为:
execution.isolation.thread.timeoutInMilliseconds >= (MaxAutoRetriesNextServer + 1) * (MaxAutoRetries + 1) * (ReadTimeout + ConnectTimeout)
由于使用线程池隔离时(execution.isolation.strategy=THREAD),会新建立一个线程,所以 tomcat 线程的变量则没法传递给新的线程。那么此时可使用 信号量隔离(execution.isolation.strategy=SEMAPHORE)。由于使用信号量隔离时,会使用 tomcat 线程调用远程服务。
其实 hystrix 也想到了会有这样的需求, 所以在 wiki 中,推荐咱们继承 HystrixConcurrencyStrategy,重写 wrapCallable() 方法。 具体代码参考 线程池隔离传递 ThreadLocal 值
该做者是基于 spi 实现的(牛逼),其实 hystrix 提供了可配置的参数供咱们配置
hystrix: plugin: HystrixConcurrencyStrategy: implementation: com.csp.hystrix.MyHystrixConcurrencyStrategy