springcloud 系列 -- 简单了解一下 hystrix

写在前面

思考的过程每每比直接获得结论更加剧要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 被大量调用时,为了保护链路,须要作限流。那么其实核心的问题是,如何进行统计。设计模式

统计

每一个 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 超时设置

设置 hystrix 超时时间时,须要大于等于 ribbon 的超时时间。 例如 ribbon 的配置以下

ReadTimeout=1000 
ConnectTimeout=1000
MaxAutoRetries=1
MaxAutoRetriesNextServer=1

那么 hystrix 的超时时间公式为:

execution.isolation.thread.timeoutInMilliseconds >= (MaxAutoRetriesNextServer + 1) * (MaxAutoRetries + 1) * (ReadTimeout + ConnectTimeout)
hystrix 使用线程池隔离时,没法传递绑定在 tomcat 线程上下文的值

由于使用线程池隔离时(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
相关文章
相关标签/搜索