Hystrix线程池隔离设计原则及接口限流实验

简介: Hystrix线程池隔离设计原则及接口限流实验

Hystrix 通过判断线程池或者信号量是否已满,超出容量的请求,直接 Reject 走降级,从而达到限流。


限流是限制对后端的服务的访问量,比如说你对 MySQL、Redis、Zookeeper 以及其它各种后端中间件的资源的访问的限制,其实是为避免过大流量直接打死后端的服务,限制服务对后端的资源的访问。


1 线程池隔离设计原则

Hystrix采取了bulkhead舱壁隔离技术,将外部依赖进行资源隔离,避免任何外部依赖的故障导致本服务崩溃。


舱壁隔离

是说将船体内部空间区隔划分成若干个隔舱,一旦某几个隔舱发生破损进水,水流不会在其间相互流动,如此一来船舶在受损时,依然能具有足够的浮力和稳定性,进而减低立即沉船的危险。

21.png

外部依赖的调用在单独的线程中执行,这样就能跟调用线程隔离开来,避免外部依赖调用timeout耗时过长,导致调用线程被卡死。


Hystrix对每个外部依赖用一个单独线程池,此时若对那个外部依赖调用延迟很严重,最多就是耗尽那个依赖自己的线程池,不会影响其他的依赖调用。


2 线程池隔离适用场景

每个服务都会调用几十个后端依赖服务

每个后端依赖服务都会提供它自己的client调用库,比如用thrift,就会提供对应的thrift依赖

client调用库随时会变更

client调用库随时可能会增加新的网络请求的逻辑

client调用库可能会包含诸如自动重试,数据解析,内存中缓存等逻辑

client调用库一般都对调用者是个黑盒,包括实现细节,网络访问,默认配置等

在生产环境,经常会出现调用者,突然发现,client调用库发生了某些变化

即使client调用库没有改变,依赖服务本身可能有会发生逻辑上的变化

有些依赖的client调用库可能还会拉取其他的依赖库,而且可能那些依赖库配置的不正确

大多数网络请求都是同步调用

调用失败和延迟,也有可能会发生在client调用库本身的代码中,不一定就是发生在网络请求中

简单来说,即默认client调用库很不靠谱!而且随时可能发生各种变化,所以就要用强制隔离的方式来确保任何服务的故障不能影响当前服务。


3 优点

任何一个依赖服务都可以被隔离在自己的线程池内,即使自己的线程池资源已满,也不影响其他的服务调用

服务可随时引入一个新的依赖服务

因为即使这个新的依赖服务有问题,也不会影响其他任何服务的调用

当一个故障的依赖服务恢复正常,可通过清理掉线程池,瞬间恢复该服务的调用,(如果是tomcat线程池被占满,重恢复就很麻烦)

若某client调用库配置出现问题,线程池的健康状况随时会报告,比如成功/失败/拒绝/超时的次数统计,然后可以近实时热修改依赖服务的调用配置,而不用停机

基于线程池的异步本质,可以在同步的调用之上,构建一层异步调用层

最大的好处就是资源隔离,确保任一依赖服务故障,不会拖垮当前服务。


4 缺点

最大的缺点:增加CPU开销

除了Tomcat本身的调用线程之外,还有hystrix自己管理的线程池

每个command执行都依托一个独立线程

会进行排队,调度,还有上下文切换

Hystrix官方做了一个多线程异步带来的额外开销,通过对比多线程异步调用+同步调用得出,Netflix API每天通过hystrix执行10亿次调用,每个服务实例有40个以上的线程池,每个线程池有10个左右的线程

用hystrix的额外开销,就是给请求带来了3ms左右的延时,最多延时在10ms以内,相比于可用性和稳定性的提升,这是可以接受的

我们可以用hystrix semaphore实现对某个依赖服务的并发访问量的限制,而不是通过线程池/队列的大小来限制流量。


sempahore可限流和削峰,但不能用来对调用延迟的服务进行timeout和隔离。

execution.isolation.strategy,设置为SEMAPHORE

hystrix就会用semaphore替代线程池机制,来对依赖服务的访问进行限流。

如果通过semaphore调用的时候,底层的网络调用延迟很严重,则无法timeout,只能一直block住。一旦请求数量超过了semephore限定的数量之后,就会立即开启限流。


5 实战接口限流

一个线程池,大小是15个,队列大小是10个,timeout时长设置的长一些,5s。


模拟发送请求,然后写死代码,在command内部做一个sleep,比如每次sleep 1s,10个请求发送过去以后,直接被hang死,线程池占满。


再发送请求,就会堵塞在缓冲队列,queue,10个,20个,10个,后10个应该就直接reject,fallback逻辑。


15 + 10 = 25个请求,15在执行,10个缓冲在队列里了,剩下的流量全部被reject,限流,降级。


withCoreSize:设置你的线程池的大小

withMaxQueueSize:设置的是你的等待队列,缓冲队列的大小

withQueueSizeRejectionThreshold:如果withMaxQueueSize<withQueueSizeRejectionThreshold,那么取的是withMaxQueueSize,反之,取得是withQueueSizeRejectionThreshold

线程池本身的大小,如果你不设置另外两个queue相关的参数,等待队列是关闭的


queue大小,等待队列的大小,timeout时长


先进去线程池的是10个请求,然后有8个请求进入等待队列,线程池里有空闲,等待队列中的请求如果还没有timeout,那么就进去线程池去执行


10 + 8 = 18个请求之外,7个请求,直接会被reject掉,限流,fallback


withExecutionTimeoutInMilliseconds(20000):timeout也设置大一些,否则如果请求放等待队列中时间太长了,直接就会timeout,等不到去线程池里执行了

withFallbackIsolationSemaphoreMaxConcurrentRequests(30):fallback,sempahore限流,30个,避免太多的请求同时调用fallback被拒绝访问


目录
相关文章
|
9月前
|
监控 Kubernetes Java
阿里面试:5000qps访问一个500ms的接口,如何设计线程池的核心线程数、最大线程数? 需要多少台机器?
本文由40岁老架构师尼恩撰写,针对一线互联网企业的高频面试题“如何确定系统的最佳线程数”进行系统化梳理。文章详细介绍了线程池设计的三个核心步骤:理论预估、压测验证和监控调整,并结合实际案例(5000qps、500ms响应时间、4核8G机器)给出具体参数设置建议。此外,还提供了《尼恩Java面试宝典PDF》等资源,帮助读者提升技术能力,顺利通过大厂面试。关注【技术自由圈】公众号,回复“领电子书”获取更多学习资料。
|
10月前
|
安全 Java API
java如何请求接口然后终止某个线程
通过本文的介绍,您应该能够理解如何在Java中请求接口并根据返回结果终止某个线程。合理使用标志位或 `interrupt`方法可以确保线程的安全终止,而处理好网络请求中的各种异常情况,可以提高程序的稳定性和可靠性。
177 6
|
11月前
|
安全 Java
在 Java 中使用实现 Runnable 接口的方式创建线程
【10月更文挑战第22天】通过以上内容的介绍,相信你已经对在 Java 中如何使用实现 Runnable 接口的方式创建线程有了更深入的了解。在实际应用中,需要根据具体的需求和场景,合理选择线程创建方式,并注意线程安全、同步、通信等相关问题,以确保程序的正确性和稳定性。
455 11
|
11月前
|
Java
java线程接口
Thread的构造方法创建对象的时候传入了Runnable接口的对象 ,Runnable接口对象重写run方法相当于指定线程任务,创建线程的时候绑定了该线程对象要干的任务。 Runnable的对象称之为:线程任务对象 不是线程对象 必须要交给Thread线程对象。 通过Thread的构造方法, 就可以把任务对象Runnable,绑定到Thread对象中, 将来执行start方法,就会自动执行Runable实现类对象中的run里面的内容。
95 1
|
11月前
|
Java 开发者
在Java多线程编程的世界里,Lock接口正逐渐成为高手们的首选,取代了传统的synchronized关键字
在Java多线程编程的世界里,Lock接口正逐渐成为高手们的首选,取代了传统的synchronized关键字
139 4
|
12月前
|
Java 开发者
在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口
【10月更文挑战第20天】在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口。本文揭示了这两种方式的微妙差异和潜在陷阱,帮助你更好地理解和选择适合项目需求的线程创建方式。
224 3
|
12月前
|
Java
在Java多线程编程中,实现Runnable接口通常优于继承Thread类
【10月更文挑战第20天】在Java多线程编程中,实现Runnable接口通常优于继承Thread类。原因包括:1) Java只支持单继承,实现接口不受此限制;2) Runnable接口便于代码复用和线程池管理;3) 分离任务与线程,提高灵活性。因此,实现Runnable接口是更佳选择。
237 2
|
12月前
|
Java
Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口
【10月更文挑战第20天】《JAVA多线程深度解析:线程的创建之路》介绍了Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口。文章详细讲解了每种方式的实现方法、优缺点及适用场景,帮助读者更好地理解和掌握多线程编程技术,为复杂任务的高效处理奠定基础。
183 2
|
12月前
|
Java 开发者
Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点
【10月更文挑战第20天】Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点,重点解析为何实现Runnable接口更具灵活性、资源共享及易于管理的优势。
273 1
|
11月前
|
Java
为什么一般采用实现Runnable接口创建线程?
因为使用实现Runnable接口的同时我们也能够继承其他类,并且可以拥有多个实现类,那么我们在拥有了Runable方法的同时也可以使用父类的方法;而在Java中,一个类只能继承一个父类,那么在继承了Thread类后我们就不能再继承其他类了。
120 0

热门文章

最新文章