设为首页 加入收藏

TOP

SpringCloud-Hystrix服务熔断与降级工作原理&源码(五)
2023-08-06 07:49:49 】 浏览:86
Tags:SpringCloud-Hystrix 源码
数hc保存了当前接口在前10s之内的请求状态(请求总数、失败数和失败率),其主要逻辑是判断请求总数是否达到阈值requestVolumeThreshold,失败率是否达到阈值errorThresholdPercentage,如果都满足,说明接口的已经足够的不稳定,需要进行熔断,则设置status为熔断开启状态,并更新circuitOpened为当前时间戳,记录上次熔断开启的时间。

五:隔离

Hystrix采用舱壁模式来隔离相互之间的依赖关系,并限制对其中任何一个的并发访问。

线程和线程池

客户端(第三方包、网络调用等)会在单独的线程执行,会与调用的该任务的线程进行隔离,以此来防止调用者调用依赖所消耗的时间过长而阻塞调用者的线程。

?[Hystrix uses separate, per-dependency thread pools as a way of constraining any given dependency so latency on the underlying executions will saturate the available threads only in that pool]

您可以在不使用线程池的情况下防止出现故障,但是这要求客户端必须能够做到快速失败(网络连接/读取超时和重试配置),并始终保持良好的执行状态。

Netflix,设计Hystrix,并且选择使用线程和线程池来实现隔离机制,有以下几个原因:

?很多应用会调用多个不同的后端服务作为依赖。

?每个服务会提供自己的客户端库包。

?每个客户端的库包都会不断的处于变更状态。

?[Client library logic can change to add new network calls]

?每个客户端库包都可能包含重试、数据解析、缓存等等其他逻辑。

?对用户来说,客户端库往往是“黑盒”的,对于实现细节、网络访问模式。默认配置等都是不透明的。

?[In several real-world production outages the determination was “oh, something changed and properties should be adjusted” or “the client library changed its behavior.]

?即使客户端本身没有改变,服务本身也可能发生变化,这些因素都会影响到服务的性能,从而导致客户端配置失效。

?传递依赖可以引入其他客户端库,这些客户端库不是预期的,也许没有正确配置。

?大部分的网络访问是同步执行的。

?客户端代码中也可能出现失败和延迟,而不仅仅是在网络调用中。

使用线程池的好处

?通过线程在自己的线程池中隔离的好处是:

简而言之,由线程池提供的隔离功能可以使客户端库和子系统性能特性的不断变化和动态组合得到优雅的处理,而不会造成中断。

注意:虽然单独的线程提供了隔离,但您的底层客户端代码也应该有超时和/或响应线程中断,而不能让Hystrix的线程池处于无休止的等待状态。

线程池的缺点

线程池最主要的缺点就是增加了CPU的计算开销,每个命令都会在单独的线程池上执行,这样的执行方式会涉及到命令的排队、调度和上下文切换。

?Netflix在设计这个系统时,决定接受这个开销的代价,来换取它所提供的好处,并且认为这个开销是足够小的,不会有重大的成本或者是性能影响。

线程成本

Hystrix在子线程执行construct()方法和run()方法时会计算延迟,以及计算父线程从端到端的执行总时间。所以,你可以看到Hystrix开销成本包括(线程、度量,日志,断路器等)。

Netflix API每天使用线程隔离的方式处理10亿多的Hystrix Command任务,每个API实例都有40多个线程池,每个线程池都有5-20个线程(大多数设置为10)

?下图显示了一个HystrixCommand在单个API实例上每秒执行60个请求(每个服务器每秒执行大约350个线程执行总数):

在中间位置(或者下线位置)不需要单独的线程池。

在第90线上,单独线程的成本为3ms。

在第99线上,单独的线程花费9ms。但是请注意,线程成本的开销增加远小于单独线程(网络请求)从2跳到28而执行时间从0跳到9的增加。

对于大多数Netflix用例来说,这样的请求在90%以上的开销被认为是可以接受的,这是为了实现韧性的好处。

对于非常低延迟请求(例如那些主要触发内存缓存的请求),开销可能太高,在这种情况下,可以使用另一种方法,如信号量,虽然它们不允许超时,提供绝大部分的有点,而不会产生开销。然而,一般来说,开销是比较小的,以至于Netflix通常更偏向于通过单独的线程来作为隔离实现。

线程隔离-信号量

上面提到了线程池隔离的缺点,当依赖延迟极低的服务时,线程池隔离技术引入的开销超过了它所带来的好处。这时候可以使用信号量隔离技术来代替,通过设置信号量来限制对任何给定依赖的并发调用量。下图说明了线程池隔离和信号量隔离的主要区别:

使用线程池时,发送请求的线程和执行依赖服务的线程不是同一个,而使用信号量时,发送请求的线程和执行依赖服务的线程是同一个,都是发起请求的线程。

您可以使用信号量(或计数器)来限制对任何给定依赖项的并发调用数,而不是使用线程池/队列大小。这允许Hystrix在不使用线程池的情况下卸载负载,但它不允许超时和离开。如果您信任客户端而您只想减载,则可以使用此方法。

HystrixCommand和HystrixObservableCommand支持2个地方的信号量:回退:当Hystrix检索回退时,它总是在调用Tomcat线程上执行此操作。执行:如果将属性execution.isolation.strategy设置为SEMAPHORE,则Hystrix将使用信号量而不是线程来限制调用该命令的并发父线程数。您可以通过定义可以执行多少并发线程的动态属性来配置信号量的这两种用法。您应该使用在调整线程池大小时使用的类似计算来调整它们的大小(以毫秒为单位返回的内存中调用可以在5000rps下执行,信号量仅为1或2 ......但默认值为10)。注意:如果依赖项与信号量隔离然后变为潜在的,则父线程将保持阻塞状态,直到基础网络调用超时。信号量拒绝将在限制被触发后开始,但填充信号量的线程无法离开。

由于Hystrix默认使用线程池做线程隔离,使用信号量隔离需要显示地将属性execution.isolation.strategy设置为ExecutionIsolationStrategy.SEMAPHORE,同时配置信号量个数,默认为10。客户端需向依赖服务发起请求时,首先要获取一个信号量才能真正发起调用,由于信号量的数量有限,当并发请求量超过信号量个数时,后续的请求都会直接拒绝,进入fallback流程。

信号量隔离主要是通过控制并发请求量,防止请求线程大面积阻塞,从而达到限流和防止雪崩的目的。

隔离总结

线程池和信号量都可以做线程隔离,但各有各的优缺点和支持的场景,对比如下:

线程切换 支持异步 支持超时 支持熔断 限流 开销
信号量
线程池

线程池和信号量都支持熔断和限流。相比线程池,信号量不需要线程

首页 上一页 2 3 4 5 6 下一页 尾页 5/6/6
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇RabbitMQ延迟队列,死信队列配置 下一篇K8S | Config应用配置

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目