服务隔离介绍
当大多数人在使用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方法,一部分正常响应