背景:

有一次在生产环境,突然出现了很多笔还款单被挂起,后来排查原因,发现是内部系统调用时出现了Hystrix调用异常。在开发过程中,因为核心线程数设置的比较大,没有出现这种异常。放到了测试环境,偶尔有出现这种情况,后来在网上查找解决方案,网上的方案是调整maxQueueSize属性就好了,当时调整了一下,确实有所改善。可没想到在生产环境跑了一段时间后却又出现这种了情况,此时我第一想法就是去查看maxQueueSize属性,可是maxQueueSize属性是设置值了。当时就比较纳闷了,为什么maxQueueSize属性不起作用,后来通过查看官方文档发现Hystrix还有一个queueSizeRejectionThreshold属性,这个属性是控制队列最大阈值的,而Hystrix默认只配置了5个,因此就算我们把maxQueueSize的值设置再大,也是不起作用的。两个属性必须同时配置

先看一下正确的Hystrix配置姿势。

application.yml:

  1. hystrix:
  2. threadpool:
  3. default:
  4. coreSize: 200 #并发执行的最大线程数,默认10
  5. maxQueueSize: 1000 #BlockingQueue的最大队列数,默认值-1
  6. queueSizeRejectionThreshold: 800 #即使maxQueueSize没有达到,达到queueSizeRejectionThreshold该值后,请求也会被拒绝,默认值5

接下来编写一个测试类,来验证几种错误配置,看看会出现什么情况。

测试类代码(A调用方):


  1. /**
  2. * @Author: XiongFeng
  3. * @Description:
  4. * @Date: Created in 11:12 2018/6/11
  5. */
  6. public class RepaymentHelperTest extends FundApplicationTests {
  7. @Autowired
  8. RepaymentHelper repaymentHelper;
  9. @Autowired
  10. private RouterFeign routerFeign;
  11. @Test
  12. public void hystrixTest() throws InterruptedException {
  13. for (int i = 0; i < 135; i++) {
  14. new Thread(new Runnable() {
  15. @Override
  16. public void run() {
  17. job();
  18. }
  19. }).start();
  20. }
  21. Thread.currentThread().join();
  22. }
  23. public void job() {
  24. String repaymentNo = "xf1002";
  25. String transNo = "T4324324234";
  26. String reqNo = "xf1002";
  27. String begintime = "20180831130030";
  28. String endtime = "20180831130050";
  29. TransRecQueryReqDto transRecQueryReqDto = new TransRecQueryReqDto();
  30. transRecQueryReqDto.setTransNo(transNo);
  31. transRecQueryReqDto.setBeginTime(begintime);
  32. transRecQueryReqDto.setEndTime(endtime);
  33. transRecQueryReqDto.setReqNo(reqNo);
  34. Resp<List<TransRecDto>> queryTransRecListResp = routerFeign.queryTransRec(new Req<>(repaymentNo, "2018080200000002", null, null, transRecQueryReqDto));
  35. System.out.println(String.format("获取结果为:【%s】", JsonUtil.toJson(queryTransRecListResp)));
  36. }
  37. }
  • 这个测试类的作用就是创建135个线程,通过RouterFeign类并发请求B服务方,看看请求结果是否出现异常。

Feign调用代码:


  1. @FeignClient(value = "${core.name}", fallbackFactory = RouterFeignBackFactory.class, path = "/router")
  2. public interface RouterFeign {
  3. /**
  4. * 代扣结果查询
  5. * @param transRecQueryReqDtoReq
  6. * @return
  7. */
  8. @PostMapping("/queryTransRec")
  9. Resp<List<TransRecDto>> queryTransRec(@RequestBody Req<TransRecQueryReqDto> transRecQueryReqDtoReq);
  10. }
  • 这个类,就是通过Feign方式去调用B服务方的客户端

服务提供方代码(B服务方):

  1. /**
  2. * @Author: XiongFeng
  3. * @Description:
  4. * @Date: Created in 16:04 2018/5/24
  5. */
  6. @Api("还款服务")
  7. @RefreshScope
  8. @RestController
  9. @RequestMapping("/router")
  10. public class TestController {
  11. private static Logger logger = LoggerFactory.getLogger(TestController.class);
  12. // 计数器
  13. private static AtomicInteger count = new AtomicInteger(1);
  14. @ApiOperation(value = "代扣结果查询")
  15. @PostMapping("/queryTransRec")
  16. Resp<List<TransRecDto>> queryTransRec(@RequestBody Req<TransRecQueryReqDto> transRecQueryReqDtoReq) throws InterruptedException {
  17. System.out.println(String.format("查询支付结果......计数: %s", count.getAndAdd(1)));
  18. Thread.sleep(500);
  19. return Resp.success(RespStatus.SUCCESS.getDesc(), null);
  20. }
  • 这个类的作用,就是一个服务提供方,计数并返回结果。

下面我们看一下几种错误的配置。

案例一(将核心线程数调低,最大队列数调大一点,但是队列拒绝阈值设置小一点):
  1. hystrix:
  2. threadpool:
  3. default:
  4. coreSize: 10
  5. maxQueueSize: 1000
  6. queueSizeRejectionThreshold: 20
此时的结果:

  • 左窗口是B服务方,右窗口是A调用方。从结果可以看出,调用135次,成功32次左右,其余线程全部抛异常。
案例二(将核心线程数调低,最大队列数调小一点,但是队列拒绝阈值设置大一点):
  1. hystrix:
  2. threadpool:
  3. default:
  4. coreSize: 10
  5. maxQueueSize: 15
  6. queueSizeRejectionThreshold: 2000
此时的结果:
  1. java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@7d6d472b rejected from java.util.concurrent.ThreadPoolExecutor@17f8bcb7[Running, pool size = 3, active threads = 3, queued tasks = 15, completed tasks = 0]

  • 左窗口是B服务方,右窗口是A调用方。从结果可以看出,调用135次,成功25次左右,其余线程全部抛异常。。
案例三(将核心线程数调低,最大队列数调大一点,但是队列拒绝阈值不设置值):
  1. hystrix:
  2. threadpool:
  3. default:
  4. coreSize: 10
  5. maxQueueSize: 1500
此时的结果:
  1. java.util.concurrent.RejectedExecutionException: Rejected command because thread-pool queueSize is at rejection threshold.

  • 左窗口是B服务方,右窗口是A调用方。此时的结果和案例一的情况一样,调用135次,成功47次左右,其余线程全部抛异常。报错跟案例一一样
案例四(将核心线程数调低,最大队列数不设值,但是队列拒绝阈值设置的比较大):
  1. hystrix:
  2. threadpool:
  3. default:
  4. coreSize: 10
  5. queueSizeRejectionThreshold: 1000
此时的结果:
  1. java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@23d268ea rejected from java.util.concurrent.ThreadPoolExecutor@66d0e2f4[Running, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0]
  2. at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)
  3. at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)
  4. at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)
  5. at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:112)

  • 左窗口是B服务方,右窗口是A调用方。此时的结果和案例二的情况一样,调用135次,成功10次左右,其余线程全部抛异常。报错跟案例二一样

下面来看一看正确的配置案例

案例一:将核心线程数调低,最大队列数和队列拒绝阈值的值都设置大一点):
  1. hystrix:
  2. threadpool:
  3. default:
  4. coreSize: 10
  5. maxQueueSize: 1500
  6. queueSizeRejectionThreshold: 1000
此时的结果:

  • 左窗口是B服务方,右窗口是A调用方。此时的结果就完全正常了,并发请求了135次,全部成功!
结论:官方默认队列阈值只有5个, 如果要调整队列,必须同时修改maxQueueSize和queueSizeRejectionThreshold属性的值,否则都会出现异常!
参考文档:

Spring Hystrix 官方文档

踩坑 Spring Cloud Hystrix 线程池队列配置的更多相关文章

  1. 危险的Hystrix线程池

    本文介绍Hystrix线程池的工作原理和参数配置,指出存在的问题并提供规避方案,阅读本文需要对Hystrix有一定的了解. 文本讨论的内容,基于hystrix 1.5.18: <dependen ...

  2. 基于Spring Boot的线程池监控方案

    前言 这篇是推动大家异步编程的思想的线程池的准备篇,要做好监控,让大家使用无后顾之忧,敬畏生产. 为什么需要对线程池进行监控 Java线程池作为最常使用到的并发工具,相信大家都不陌生,但是你真的确定使 ...

  3. 第五章 服务容错保护:Spring Cloud Hystrix

    在微服务架构中,我们将系统拆分为很多个服务,各个服务之间通过注册与订阅的方式相互依赖,由于各个服务都是在各自的进程中运行,就有可能由于网络原因或者服务自身的问题导致调用故障或延迟,随着服务的积压,可能 ...

  4. Spring Cloud Hystrix 服务容错保护

    目录 一.Hystrix 是什么 二.Hystrix断路器搭建 三.断路器优化 一.Hystrix 是什么 ​ 在微服务架构中,我们将系统拆分成了若干弱小的单元,单元与单元之间通过HTTP或者TCP等 ...

  5. 7、Spring Cloud Hystrix

    1.Spring Cloud Hystrix简介 (1).分布式问题 复杂分布式体系结构中的应用程序有数十个依赖关系,每个依赖关系在某些时候将不可避免地失败. 多个微服务之间调用的时候,假设微服务A调 ...

  6. Spring Cloud 微服务笔记(六)Spring Cloud Hystrix

    Spring Cloud Hystrix Hystrix是一个延迟和容错库,旨在隔离远程系统.服务和第三方库,阻止链接故障,在复杂的分布式系统中实现恢复能力. 一.快速入门 1)依赖: <dep ...

  7. spring cloud: Hystrix(二):简单使用@HystrixCommand的commandProperties配置@HistrixProperty隔离策略

    spring cloud: Hystrix(二):简单使用@HystrixCommand的commandProperties配置@HistrixProperty隔离策略 某电子商务网站在一个黑色星期五 ...

  8. Spring Cloud Hystrix理解与实践(一):搭建简单监控集群

    前言 在分布式架构中,所谓的断路器模式是指当某个服务发生故障之后,通过断路器的故障监控,向调用方返回一个错误响应,这样就不会使得线程因调用故障服务被长时间占用不释放,避免故障的继续蔓延.Spring ...

  9. 分布式系统的延时和故障容错之Spring Cloud Hystrix

    本示例主要介绍 Spring Cloud 系列中的 Eureka,如何使用Hystrix熔断器容错保护我们的应用程序. 在微服务架构中,系统被拆分成很多个服务单元,各个服务单元的应用通过 HTTP 相 ...

随机推荐

  1. Model-View-Controller Explained in C++

    The Permanent URL is: Model-View-Controller Explained in C++. The Model-View-Controller (MVC) is not ...

  2. hive表批处理

    对hive中的表进行批量处理,如下是一个简单的脚本 #给定一个hive数据库名,生成它的所有表的create SQL语句,并导出到文件 create_fun(){ hive -e } #显示一个表中所 ...

  3. python连接数据库(2)——mongodb

    mongodb是近一段时间以来比较流行的非关系数据库之一,由于python和它都对json类型有着很好的支持,因此配合起来可谓天衣无缝. 首先要下载python对mongodb支持的包pymongo ...

  4. WebP 大战 JPEG,谁才是真正的王者?

    目前在互联网上,图片流量仍占据较大的一部分.因此,在保证图片质量不变的情况下,节省流量带宽是大家一直需要去解决的问题.传统的图片格式,如 JPEG,PNG,GIF 等格式图片已经没有太多的优化空间.因 ...

  5. kubernetes实战篇之nexus oss服务器部署及基于nexus的docker镜像仓库搭建

    系列目录 Nexus oss仓库管理平台搭建 Nexus是一款仓库管理工具,支持Npm,bower,maven,nuget,apt,yum甚至docker,helm等各种仓库,说的通俗以下,就是私服镜 ...

  6. 03 我的第一个html页面

    <!--定义文档的类型,一个html就是一个文档--> <!DOCTYPE html> <html lang="en"> <!--head ...

  7. sql server使用公用表表达式CTE通过递归方式编写通用函数自动生成连续数字和日期

    问题:在数据库脚本开发中,有时需要生成一堆连续数字或者日期,例如yearly report就需要连续数字做年份,例如daily report就需要生成一定时间范围内的每一天日期.而自带的系统表mast ...

  8. 程序员修仙之路--优雅快速的统计千万级别uv(留言送书)

    菜菜,咱们网站现在有多少PV和UV了? Y总,咱们没有统计pv和uv的系统,预估大约有一千万uv吧 写一个统计uv和pv的系统吧 网上有现成的,直接接入一个不行吗? 别人的不太放心,毕竟自己写的,自己 ...

  9. 深入V8引擎-AST(1)

    没办法了,开坑吧,接下来的几篇会讲述JavaScript字符串源码在v8中转换成AST(抽象语法树)的过程. JS代码在V8的解析只有简单的几步,其中第一步就是将源字符串转换为抽象语法树,非常类似于v ...

  10. Scala 学习之路(十)—— 函数 & 闭包 & 柯里化

    一.函数 1.1 函数与方法 Scala中函数与方法的区别非常小,如果函数作为某个对象的成员,这样的函数被称为方法,否则就是一个正常的函数. // 定义方法 def multi1(x:Int) = { ...