Hystrix 断路器,先说说为什么要用断路器。
在微服务架构中, 我们将系统拆分成了很多服务单元, 各单元的应用间通过服务注册与订阅的方式互相依赖。 由于每个单元都在不同的进程中运行, 依赖通过远程调用的方式执行, 这样就有可能因为网络原因或是依赖服务自身间题出现调用故障或延迟, 而这些问题会直接导致调用方的对外服务也出现延迟, 若此时调用方的请求不断增加, 最后就会因等待出现故障的依赖方响应形成任务积压,最终导致自身服务的瘫痪。
可能会好奇为何自身的服务也会瘫痪,原因就是线程资源是有限的,当被调用的微服务阻塞时,该线程的资源无法立即释放,随着请求数量的不断增加,线程资源就会被耗尽,那么该服务也就瘫痪了。
为了防止一个微服务不可用导致多个微服务瘫痪,就引入Hystrix断路器。
Hystrix的好处:
Hystrix具备服务降级、 服务熔断、 线程和信号隔离、 请求缓存、 请求合并以及服务监控等强大功能。
假设有微服务A,被调用微服务B,被调用微服务C
服务熔断:每一个服务都应该具有服务熔断的功能这个图是网上的
服务的健康状况 = 请求失败数 / 请求总数. 默认是50% 10秒钟之内达到20个请求,如果只有19个请求,就算19个请求都失败也不会熔断
熔断器开关由关闭到打开的状态转换是通过当前服务健康状况和设定阈值比较决定的
1.当熔断器开关关闭时, 请求被允许通过熔断器. 如果当前健康状况高于设定阈值, 开关继续保持关闭. 如果当前健康状况低于设定阈值, 开关则切换为打开状态.
2.当熔断器开关打开时, 请求被禁止通过.
3.当熔断器开关处于打开状态, 经过一段时间后(5秒钟), 熔断器会自动进入半开状态, 这时熔断器只允许一个请求通过. 当该请求调用成功时, 熔断器恢复到关闭状态. 若该请求失败, 熔断器继续保持打开状态, 接下来的请求被禁止通过。
熔断器的开关能保证服务调用者在调用异常服务时, 快速返回结果, 避免大量的同步等待. 并且熔断器能在一段时间后继续侦测请求执行结果, 提供恢复服务调用的可能。
举个栗子:
当A服务调用B服务的时候,假设有大量的请求,然后B服务每一个请求都会超时(Hystrix 默认超时时间为 2000 毫秒),或者说请求直接失败,那么此时就会触发熔断,所有发送到B服务的请求都不在发送,默认发送失败。从此时开始到5s后,会再次让请求去调用B服务,如果此请求依旧超时或者失败,那么B服务继续熔断。还有半开模式,可以让部分的请求通过,这样就可以达到限流的效果。
服务降级:
1.当前命令处于 “熔断/短路 ” 状态, 断路器是打开的时候。
2. 当前命令的线程池、 请求队列或 者信号量被占满的时候。
3.HystixObservableCommand.construct()或HystrixCommand.run()抛出异常的时候。
在服务降级逻辑中, 我们需要实现一个通用的响应结果,并且该结果的处理逻辑应当是从缓存或是根据一些静态逻辑来获取,而不是依赖网络请求获取。如果一定要在降级逻辑中包含网络请求,那么该请求也必须被包装在HystrixCommand或是HystrixObservableCommand中, 从而形成级联的降级策略, 而最终的降级逻辑一定不是一个依赖网络请求的处理, 而是一个能够稳定地返回结果的处理逻辑。
举个栗子:
当A服务去调用B服务的时候,A服务发现B服务不可用,但是此时A服务需要一个结果,那么就会进入A服务自定义的进行服务降级的方法,在此方法中就可以直接返回一个页面等等,告诉用户该服务维修中或者网络不好之类的异常,也可以调用别的服务,但是调用别的服务也需要做二次降级,除非你认为你调用的服务十分可靠不会出错。
线程和信号隔离:其实线程和信号隔离是差不多的,只是在服务可靠的时候使用信号量可以减少开销。
“ 舱壁模式” 对于熟悉 Docker 的读者一定不陌生, Docker 通过 “ 舱壁模式” 实现进程的隔离, 使得容器与容器之间不会互相影响。 而 Hystrix 则使用该模式实现线程池的隔离,它会为每一个依赖服务创建一个独立的线程池, 这样就算某个依赖服务出现延迟过高的情况, 也只是对该依赖服务的调用产生影响, 而不会拖慢其他的依赖服务。
好处:
1.应用自身得到完全保护, 不会受不可控的依赖服务影响。 即便给依赖服务分配的线程池被填满, 也不会影响应用自身的其余部分。
2.可以有效降低接入新服务的风险。 如果新服务接入后运行不稳定或存在问题, 完全不会影响应用其他的请求。
3.当依赖的服务从失效恢复正常后, 它的线程池会被清理并且能够马上恢复健康的服务, 相比之下, 容器级别的清理恢复速度要慢得多。
4. 当依赖的服务出现配置错误的时候, 线程池会快速反映出此问题(通过失败次数、延迟、超时、拒绝等指标的增加情况)。 同时, 我们可以在不影响应用功能的情况下通过实时的动态属性刷新(后续会通过Spring Cloud Config与Spring Cloud Bus的联合使用来介绍) 来处理它。
5. 当依赖的服务因实现机制调整等原因造成其性能出现很大变化的时候, 线程池的监控指标信息会反映出这样的变化。 同时, 我们也可以通过实时动态刷新自身应用对依赖服务的阙值进行调整以适应依赖方的改变。
6. 除了上面通过线程池隔离服务发挥的优点之外, 每个专有线程池都提供了内置的并发实现, 可以利用它为同步的依赖服务构建异步访问。
举个栗子:
在A服务中需要调用B服务和C服务,那么通过线程隔离,我们可以给A->B,A->C分别分配50个线程量(假设我们有100个线程,看情况分配),那么就算B服务宕机了,也最多只会影响我50个线程不会导致A服务不可用,通俗点说就算各玩各的,互不干扰。
请求缓存:Hystrix的请求缓存比较鸡肋,请求缓存的范围是Request域。
缓存这种也没什么好解释的。
请求合并:
微服务架构中的依赖通常通过远程调用实现, 而远程调用中最常见的问题就是通信消耗与连接数占用。 在高并发的情况之下,
因通信次数的增加, 总的通信时间消耗将会变得不那么理想。 同时, 因为依赖服务的线程池资源有限,将出现排队等待与响应
延迟的清况。
为了优化这两个问题, Hystrix 提供了 HystrixCollapser 来实现请求的合并,以减少通信消耗和线程数的占用。微服务架构中
的依赖通常出现排队等待。
举个栗子:
服务A需要调用服务B,这个请求来的很快,10ms中有5次请求。一般情况下我们需要去调用5次B服务,但是用了请求合并就只会调用一次B服务,减少了请求的开销。HystrixCollapser 默认合并10毫秒内的请求,但是这个会增加服务返回的时间。
服务监控:Hystrix仪表盘Hystrix Dashboard 下面会仔细讲解的
努力吧,皮卡丘