美文网首页
Hystrix源码

Hystrix源码

作者: 段永平 | 来源:发表于2020-03-22 11:09 被阅读0次

hystrix是什么?

在微服务场景中,通常会有很多层的服务调用。如果一个底层服务出现问题,故障会被向上传播给用户。我们需要一种机制,当底层服务不可用时,可以阻断故障的传播。这就是断路器的作用。他是系统服务稳定性的最后一重保障。

Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时、异常等,Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。

“断路器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要地占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。

在springcloud中断路器组件就是Hystrix。Hystrix也是Netflix套件的一部分。他的功能是,当对某个服务的调用在一定的时间内(默认10s,由metrics.rollingStats.timeInMilliseconds配置),有超过一定次数(默认20次,由circuitBreaker.requestVolumeThreshold参数配置)并且失败率超过一定值(默认50%,由circuitBreaker.errorThresholdPercentage配置),该服务的断路器会打开。返回一个由开发者设定的fallback,fallback可以是另一个由Hystrix保护的服务调用,也可以是固定的值。fallback也可以设计成链式调用,先执行某些逻辑,再返回fallback。

Hystrix设计目标

对来自依赖的延迟和故障进行防护和控制——这些依赖通常都是通过网络访问的

阻止故障的连锁反应

快速失败并迅速恢复

回退并优雅降级

提供近实时的监控与告警

Hystrix遵循的设计原则

防止任何单独的依赖耗尽资源(线程)

过载立即切断并快速失败,防止排队

尽可能提供回退以保护用户免受故障

使用隔离技术(例如隔板,泳道和断路器模式)来限制任何一个依赖的影响

通过近实时的指标,监控和告警,确保故障被及时发现

通过动态修改配置属性,确保故障及时恢复

防止整个依赖客户端执行失败,而不仅仅是网络通信

Hystrix如何实现这些设计目标?

使用命令模式将所有对外部服务(或依赖关系)的调用包装在HystrixCommand或HystrixObservableCommand对象中,并将该对象放在单独的线程中执行;

每个依赖都维护着一个线程池(或信号量),线程池被耗尽则拒绝请求(而不是让请求排队)。

记录请求成功,失败,超时和线程拒绝。

服务错误百分比超过了阈值,熔断器开关自动打开,一段时间内停止对该服务的所有请求。

请求失败,被拒绝,超时或熔断时执行降级逻辑。

近实时地监控指标和配置的修改。

如何使用

Netflix断路器是安装在服务消费者上。我们需要做的是在服务消费者上开启断路器并配置。

1),引入依赖

2),打开开关

只需要在启动类上加上@EnableHystrix注解即可

3),在服务上加上fallback

Hystrix处理流程

Hystrix流程图如下:

Hystrix整个工作流如下:

1.构造一个 HystrixCommand或HystrixObservableCommand对象,用于封装请求,并在构造方法配置请求被执行需要的参数;

2.执行命令,Hystrix提供了4种执行命令的方法,后面详述;

3.判断是否使用缓存响应请求,若启用了缓存,且缓存可用,直接使用缓存响应请求。Hystrix支持请求缓存,但需要用户自定义启动;

4.判断熔断器是否打开,如果打开,跳到第8步;

5.判断线程池/队列/信号量是否已满,已满则跳到第8步;

6.执行HystrixObservableCommand.construct()或HystrixCommand.run(),如果执行失败或者超时,跳到第8步;否则,跳到第9步;

7.统计熔断器监控指标;

8.走Fallback备用逻辑

9.返回请求响应

从流程图上可知道,第5步线程池/队列/信号量已满时,还会执行第7步逻辑,更新熔断器统计信息,而第6步无论成功与否,都会更新熔断器统计信息。

执行命令的几种方法

Hystrix提供了4种执行命令的方法,execute()和queue() 适用于HystrixCommand对象,而observe()和toObservable()适用于HystrixObservableCommand对象。

execute():同步堵塞方式执行run(),只支持接收一个值对象。hystrix会从线程池中取一个线程来执行run(),并等待返回值。

queue():异步非阻塞方式执行run(),只支持接收一个值对象。调用queue()就直接返回一个Future对象。可通过 Future.get()拿到run()的返回结果,但Future.get()是阻塞执行的。若执行成功,Future.get()返回单个返回值。当执行失败时,如果没有重写fallback,Future.get()抛出异常。

observe():事件注册前执行run()/construct(),支持接收多个值对象,取决于发射源。调用observe()会返回一个hot Observable,也就是说,调用observe()自动触发执行run()/construct(),无论是否存在订阅者。

如果继承的是HystrixCommand,hystrix会从线程池中取一个线程以非阻塞方式执行run();如果继承的是HystrixObservableCommand,将以调用线程阻塞执行construct()。

observe()使用方法:调用observe()会返回一个Observable对象;调用这个Observable对象的subscribe()方法完成事件注册,从而获取结果

toObservable():事件注册后执行run()/construct(),支持接收多个值对象,取决于发射源。调用toObservable()会返回一个cold Observable,也就是说,调用toObservable()不会立即触发执行run()/construct(),必须有订阅者订阅Observable时才会执行。

如果继承的是HystrixCommand,hystrix会从线程池中取一个线程以非阻塞方式执行run(),调用线程不必等待run();如果继承的是HystrixObservableCommand,将以调用线程堵塞执行construct(),调用线程需等待construct()执行完才能继续往下走。

toObservable()使用方法:调用toObservable()会返回一个Observable对象;调用这个Observable对象的subscribe()方法完成事件注册,从而获取结果

需注意的是,HystrixCommand也支持toObservable()和observe(),但是即使将HystrixCommand转换成Observable,它也只能发射一个值对象。只有HystrixObservableCommand才支持发射多个值对象。

几种方法的关系

execute()实际是调用了queue().get()

queue()实际调用了toObservable().toBlocking().toFuture()

observe()实际调用toObservable()获得一个cold Observable,再创建一个ReplaySubject对象订阅Observable,将源Observable转化为hot Observable。因此调用observe()会自动触发执行run()/construct()。

Hystrix总是以Observable的形式作为响应返回,不同执行命令的方法只是进行了相应的转换。

Hystrix源码

我们在前面的章节提到过,在maven中引入Hystrix的依赖,然后在启动类上加上@EnableHystrix注解,这个注解它做了什么呢?在EnableHystrix注解中又有一个@EnableCircuitBreaker的注解,在EnableCircuitBreaker中通过Import注解注入了一个叫EnableCircuitBreakerImportSelector的类,这个类实现了SpringFactoryImportSelector,SpringFactoryImportSelector间接继承了ImportSelector,ImportSelector是spring提供的一种动态注入bean的方式。SpringFactoryImportSelector是Spring Cloud提供的对ImportSelector的一种实现,通过这个类实现了Hystrix的动态插拔。

何谓Hystrix动态插拔?

我们先看一下spring cloud的hystrix报的spring.factory配置类:

如上图所示,HystrixCircuitBreakerConfiguration的配置类并没有包含在spring boot自动扫描的EnableAutoConfiguration配置中,而是自定义了一个配置EnableCircuitBreaker,如果交给spring boot管理,则会在项目启动的时候就会被spring boot自动加载。EnableCircuitBreaker这个配置是在SpringFactoryImportSelector的importSelectors中通过调用SpringFactoriesLoader.loadFactoryNames将HystrixCircuitBreakerConfiguration加载到spring容器中。

总结:在启动类中加上@EnableHystrix注解,其实就是注入了HystrixCircuitBreakerConfiguration这个类到spring容器中。

在HystrixCircuitBreakerConfiguration注入了一个切面bean:HystrixCommandAspect.

在HystrixCommandAspect中,定义了两个切点HystrixCommand和HystrixCollapser以及一个环绕通知,HystrixCollapser是用来进行请求合并的。如果同时加了HystrixCommand和HystrixCollapser注解,直接抛出异常;在META_HOLDER_FACTORY_MAP中包含了两个值,就是HystrixCommand和HystrixCollapser,通过META_HOLDER_FACTORY_MAP.get来获取加的到底是哪个注解。

熔断

现实生活中,可能大家都有注意到家庭电路中通常会安装一个保险盒,当负载过载时,保险盒中的保险丝会自动熔断,以保护电路及家里的各种电器,这就是熔断器的一个常见例子。Hystrix中的熔断器(Circuit Breaker)也是起类似作用,Hystrix在运行过程中会向每个commandKey对应的熔断器报告成功、失败、超时和拒绝的状态,熔断器维护并统计这些数据,并根据这些统计信息来决策熔断开关是否打开。如果打开,熔断后续请求,快速返回。隔一段时间(默认是5s)之后熔断器尝试半开,放入一部分流量请求进来,相当于对依赖服务进行一次健康检查,如果请求成功,熔断器关闭。

熔断器配置

Circuit Breaker主要包括如下6个参数:

1、circuitBreaker.enabled

是否启用熔断器,默认是TRUE。可以直接关闭,关闭之后返回一个NoOpCircuitBreaker。

2 、circuitBreaker.forceOpen

熔断器强制打开,始终保持打开状态,不关注熔断开关的实际状态。默认值FLASE。

3、circuitBreaker.forceClosed

熔断器强制关闭,始终保持关闭状态,不关注熔断开关的实际状态。默认值FLASE。

4、circuitBreaker.errorThresholdPercentage

错误率,默认值50%,例如一段时间(10s)内有100个请求,其中有54个超时或者异常,那么这段时间内的错误率是54%,大于了默认值50%,这种情况下会触发熔断器打开。

5、circuitBreaker.requestVolumeThreshold

默认值20。含义是一段时间内至少有20个请求才进行errorThresholdPercentage计算。比如一段时间了有19个请求,且这些请求全部失败了,错误率是100%,但熔断器不会打开,总请求数不满足20。

6、circuitBreaker.sleepWindowInMilliseconds

半开状态试探睡眠时间,默认值5000ms。如:当熔断器开启5000ms之后,会尝试放过去一部分流量进行试探,确定依赖服务是否恢复。

熔断器工作原理

下图展示了HystrixCircuitBreaker的工作原理:

熔断器工作的详细过程如下:

第一步,调用allowRequest()判断是否允许将请求提交到线程池

如果熔断器强制打开,circuitBreaker.forceOpen为true,不允许放行,返回。

如果熔断器强制关闭,circuitBreaker.forceClosed为true,允许放行。此外不必关注熔断器实际状态,也就是说熔断器仍然会维护统计数据和开关状态,只是不生效而已。

第二步,调用isOpen()判断熔断器开关是否打开

如果熔断器开关打开,进入第三步,否则继续;

如果一个周期内总的请求数小于circuitBreaker.requestVolumeThreshold的值,允许请求放行,否则继续;

如果一个周期内错误率小于circuitBreaker.errorThresholdPercentage的值,允许请求放行。否则,打开熔断器开关,进入第三步。

第三步,调用allowSingleTest()判断是否允许单个请求通行,检查依赖服务是否恢复

如果熔断器打开,且距离熔断器打开的时间或上一次试探请求放行的时间超过circuitBreaker.sleepWindowInMilliseconds的值时,熔断器器进入半开状态,允许放行一个试探请求;否则,不允许放行。

此外,为了提供决策依据,每个熔断器默认维护了10个bucket,每秒一个bucket,当新的bucket被创建时,最旧的bucket会被抛弃。其中每个blucket维护了请求成功、失败、超时、拒绝的计数器,Hystrix负责收集并统计这些计数器。

在获取熔断器时,判断熔断器是否为空,如果不为空,则说明有缓存;如果为空,则构建一个新的熔断器。

总结:在程序入口的配置类里面,定义了AOP的切面,通过环绕通知around代理Hystrix的注解,所有加了注解的方法都会进入代理方法中,在代理方法中,会读取系统的配置、注解的配置,构建一个Command对象,在Command对象构建的中途,会构建熔断器组件和线程池等等一系列的配置的初始化,配置来源都是yml+注解上面的配置;可以根据Command的key把多个注解绑定起来,这样就不用重复构建多个Command对象。

资源隔离

资源隔离主要指对线程的隔离。Hystrix提供了两种线程隔离方式:线程池和信号量。

线程隔离-线程池

Hystrix通过命令模式对发送请求的对象和执行请求的对象进行解耦,将不同类型的业务请求封装为对应的命令请求。如订单服务查询商品,查询商品请求->商品Command;商品服务查询库存,查询库存请求->库存Command。并且为每个类型的Command配置一个线程池,当第一次创建Command时,根据配置创建一个线程池,并放入ConcurrentHashMap,如商品Command:

后续查询商品的请求创建Command时,将会重用已创建的线程池。线程池隔离之后的服务依赖关系:

通过将发送请求线程与执行请求的线程分离,可有效防止发生级联故障。当线程池或请求队列饱和时,Hystrix将拒绝服务,使得请求线程可以快速失败,从而避免依赖问题扩散。

线程池隔离优缺点

优点:

1.保护应用程序以免受来自依赖故障的影响,指定依赖线程池饱和不会影响应用程序的其余部分。

2.当引入新客户端lib时,即使发生问题,也是在本lib中,并不会影响到其他内容。

3。当依赖从故障恢复正常时,应用程序会立即恢复正常的性能。

4.当应用程序一些配置参数错误时,线程池的运行状况会很快检测到这一点(通过增加错误,延迟,超时,拒绝等),同时可以通过动态属性进行实时纠正错误的参数配置。

5.如果服务的性能有变化,需要实时调整,比如增加或者减少超时时间,更改重试次数,可以通过线程池指标动态属性修改,而且不会影响到其他调用请求。

6.除了隔离优势外,hystrix拥有专门的线程池可提供内置的并发功能,使得可以在同步调用之上构建异步门面(外观模式),为异步编程提供了支持(Hystrix引入了Rxjava异步框架)。

注意:尽管线程池提供了线程隔离,我们的客户端底层代码也必须要有超时设置或响应线程中断,不能无限制的阻塞以致线程池一直饱和。

缺点:

线程池的主要缺点是增加了计算开销。每个命令的执行都在单独的线程完成,增加了排队、调度和上下文切换的开销。因此,要使用Hystrix,就必须接受它带来的开销,以换取它所提供的好处。

通常情况下,线程池引入的开销足够小,不会有重大的成本或性能影响。但对于一些访问延迟极低的服务,如只依赖内存缓存,线程池引入的开销就比较明显了,这时候使用线程池隔离技术就不适合了,我们需要考虑更轻量级的方式,如信号量隔离。

参考文章:https://my.oschina.net/7001/blog/1619842

相关文章

  • 7、Hystrix与HystrixDashboard

    一、Hystrix的概述 Hystrix 在 GitHub上的源码地址:https://github.com/Ne...

  • 降级熔断框架 Hystrix 源码解析:滑动窗口统计

    [TOC] 降级熔断框架 Hystrix 源码解析:滑动窗口统计 概述 Hystrix 是一个开源的降级熔断框架,...

  • Hystrix源码

    hystrix是什么? 在微服务场景中,通常会有很多层的服务调用。如果一个底层服务出现问题,故障会被向上传播给用户...

  • Hystrix工作流程解析

    搭建Hystrix源码阅读环境 引入依赖 创建Command类 创建测试类 Hystrix工作流程 首先我们看一下...

  • Spring Cloud——Hystrix源码分析

    spring-cloud-netflix-hystrix 原理源码分析: 本文主要针对 spring-cloud-...

  • Hystrix熔断器执行机制

    本篇假设大家对Hystrix的执行过程及源码有一定的了解,这里介绍Hystrix的熔断器执行机制。 1.Hystr...

  • Hystrix源码解析

    Hystrix简介   在分布式系统中,难免有对外部接口的依赖,而外部接口有可能出现响应缓慢,大量请求超时,大量访...

  • Hystrix源码解析

    https://my.oschina.net/7001/blog/1619842https://segmentfa...

  • Hystrix源码解析

    Hystrix的用途以及使用场景就不在这里赘述了,这里只关注源码级别的实现原理。 1、AbstractComman...

  • Hystrix源码解析

    什么是HystrixHystrix是Spring Cloud提供的一种带有熔断机制的框架,由于在微服务系统中同一个...

网友评论

      本文标题:Hystrix源码

      本文链接:https://www.haomeiwen.com/subject/kjsmyhtx.html