Hystrix系列之信号量、线程池
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Hystrix系列之信号量、线程池相关的知识,希望对你有一定的参考价值。
参考技术A Hystrix内部提供了两种模式执行逻辑:信号量、线程池。默认情况下,Hystrix使用线程池模式。
不过两者有什么区别,在实际场景中如何选择?
如果要使用信号量模式,需要配置参数 execution.isolation.strategy = ExecutionIsolationStrategy.SEMAPHORE .
在该模式下,接收请求和执行下游依赖在同一个线程内完成,不存在线程上下文切换所带来的性能开销,所以大部分场景应该选择信号量模式,但是在下面这种情况下,信号量模式并非是一个好的选择。
比如一个接口中依赖了3个下游:serviceA、serviceB、serviceC,且这3个服务返回的数据互相不依赖,这种情况下如果针对A、B、C的熔断降级使用信号量模式,那么接口耗时就等于请求A、B、C服务耗时的总和,无疑这不是好的方案。
另外,为了限制对下游依赖的并发调用量,可以配置Hystrix的 execution.isolation.semaphore.maxConcurrentRequests ,当并发请求数达到阈值时,请求线程可以快速失败,执行降级。
实现也很简单,一个简单的计数器,当请求进入熔断器时,执行 tryAcquire() ,计数器加1,结果大于阈值的话,就返回false,发生信号量拒绝事件,执行降级逻辑。当请求离开熔断器时,执行 release() ,计数器减1。
在该模式下,用户请求会被提交到各自的线程池中执行,把执行每个下游服务的线程分离,从而达到资源隔离的作用。当线程池来不及处理并且请求队列塞满时,新进来的请求将快速失败,可以避免依赖问题扩散。
在信号量模式提到的问题,对所依赖的多个下游服务,通过线程池的异步执行,可以有效的提高接口性能。
优势
缺点
因为Hystrix默认使用了线程池模式,所以对于每个Command,在初始化的时候,会创建一个对应的线程池,如果项目中需要进行降级的接口非常多,比如有上百个的话,不太了解Hystrix内部机制的同学,按照默认配置直接使用,可能就会造成线程资源的大量浪费。
SpringBootSpringBoot 之 Hystrix服务隔离
服务隔离介绍
当大多数人在使用Tomcat时,多个HTTP服务会共享一个线程池,假设其中一个HTTP服务访问的数据库响应非常慢,这将造成服务响应时间延迟增加,大多数线程阻塞等待数据响应返回,导致整个Tomcat线程池都被该服务占用,甚至拖垮整个Tomcat。因此,如果我们能把不同HTTP服务隔离到不同的线程池,则某个HTTP服务的线程池满了也不会对其他服务造成灾难性故障。这就需要线程隔离或者信号量隔离来实现了。
使用线程隔离或信号隔离的目的是为不同的服务分配一定的资源,当自己的资源用完,直接返回失败而不是占用别人的资源。
Hystrix实现服务隔离两种方案
Hystrix的资源隔离策略有两种,分别为:线程池和信号量。
线程池方式
优点:
1、 使用线程池隔离可以完全隔离第三方应用,请求线程可以快速放回。
2、 请求线程可以继续接受新的请求,如果出现问题线程池隔离是独立的不会影响其他应用。
3、 当失败的应用再次变得可用时,线程池将清理并可立即恢复,而不需要一个长时间的恢复。
4、 独立的线程池提高了并发性
缺点:
线程池隔离的主要缺点是它们增加计算开销(CPU)。每个命令的执行涉及到排队、调度和上 下文切换都是在一个单独的线程上运行的。
线程池方式案例
1、使用上一章项目工程,在服务端项目的业务类Service中编写如下方法:
1 // 服务限流 2 @HystrixCommand(fallbackMethod = "paymentCircuitBreaker_Thread", 3 // 属性设置参考:HystrixCommandProperties 4 commandProperties = { 5 // 隔离策略,有THREAD和SEMAPHORE 6 @HystrixProperty(name="execution.isolation.strategy", value="THREAD") 7 }, 8 threadPoolProperties = { 9 // 线程池核心线程数 10 @HystrixProperty(name = "coreSize", value = "3"), 11 // 队列最大长度 12 @HystrixProperty(name = "maxQueueSize", value = "5"), 13 // 排队线程数量阈值,默认为5,达到时拒绝,如果配置了该选项,队列的大小是该队列 14 @HystrixProperty(name = "queueSizeRejectionThreshold", value = "7") 15 }) 16 public String paymentCircuitBreakerThread(@PathVariable("id") Integer id){ 17 int second = 500; 18 try { 19 // 休眠500毫秒 20 TimeUnit.MILLISECONDS.sleep(second); 21 } catch (InterruptedException e) { 22 // e.printStackTrace(); 23 } 24 // 异常 25 // int n = 10/0; 26 String result = "线程池:" + Thread.currentThread().getName() 27 + ",paymentCircuitBreakerThreadPool,ID == " + id 28 + ",耗时" + second + "毫秒"; 29 return result; 30 } 31 32 public String paymentCircuitBreaker_Thread(@PathVariable("id") Integer id){ 33 return " paymentCircuitBreaker_Thread 服务限流,ID == " + id; 34 }
2、在controller中调用该方法
1 // 线程 2 @GetMapping(value = "/payment/hystrix/thread/{id}") 3 public String paymentCircuitBreakerThreadPool(@PathVariable("id") Integer id) { 4 String result = paymentService.paymentCircuitBreakerThread(id); 5 log.info("result===" + result); 6 return result; 7 }
3、重启项目,使用JMeter进行并发测试,测试url地址:http://localhost:8008/payment/hystrix/thread/1
测试发现,有一部分请求调用了fallback方法,一部分正常响应
信号量方式
使用一个原子计数器(或信号量)来记录当前有多少个线程在运行,当请求进来时先判断计数 器的数值,若超过设置的最大线程个数则拒绝该请求,若不超过则通行,这时候计数器+1,请求返 回成功后计数器-1。
与线程池隔离最大不同在于执行依赖代码的线程依然是请求线程
提示:信号量的大小可以动态调整, 线程池大小不可以
信号量方式案例
1、使用上一章项目工程,在服务端项目的业务类Service中编写如下方法:
1 // 服务限流 2 @HystrixCommand(fallbackMethod = "paymentCircuitBreaker_Semaphore", 3 // 属性设置参考:HystrixCommandProperties 4 commandProperties = { 5 // 隔离策略,有THREAD和SEMAPHORE 6 // THREAD - 它在单独的线程上执行,并发请求受线程池中的线程数量的限制(默认) 7 // SEMAPHORE - 它在调用线程上执行,并发请求受到信号量计数的限制 8 @HystrixProperty(name="execution.isolation.strategy", value="SEMAPHORE"), 9 // 设置在使用时允许到HystrixCommand.run()方法的最大请求数。默认值:10 ,SEMAPHORE模式有效 10 @HystrixProperty(name="execution.isolation.semaphore.maxConcurrentRequests", value="1") 11 12 }) 13 public String paymentCircuitBreakerSemaphore(@PathVariable("id") Integer id){ 14 int second = 500; 15 try { 16 // 休眠500毫秒 17 TimeUnit.MILLISECONDS.sleep(second); 18 } catch (InterruptedException e) { 19 // e.printStackTrace(); 20 } 21 // 异常 22 // int n = 10/0; 23 String result = "线程池:" + Thread.currentThread().getName() 24 + ",paymentCircuitBreaker_Semaphore,ID == " + id 25 + ",耗时" + second + "毫秒"; 26 return result; 27 } 28 29 public String paymentCircuitBreaker_Semaphore(@PathVariable("id") Integer id){ 30 return " paymentCircuitBreaker_Semaphore 服务限流,ID == " + id; 31 }
2、在controller中调用该方法
1 // 信号量 2 @GetMapping(value = "/payment/hystrix/semaphore/{id}") 3 public String paymentCircuitBreakerLimit(@PathVariable("id") Integer id) { 4 String result = paymentService.paymentCircuitBreakerSemaphore(id); 5 log.info("result===" + result); 6 return result; 7 }
3、重启项目,使用JMeter进行并发测试,测试url地址:http://localhost:8008/payment/hystrix/semaphore/1
测试发现,有一部分请求调用了fallback方法,一部分正常响应
以上是关于Hystrix系列之信号量、线程池的主要内容,如果未能解决你的问题,请参考以下文章