zoukankan      html  css  js  c++  java
  • 踩坑 Spring Cloud Hystrix 线程池队列配置

    原文:https://www.cnblogs.com/seifon/p/9921774.html

    背景:

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

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

    application.yml:

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

     

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

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

    Copy
    
    /**
     * @Author: XiongFeng
     * @Description:
     * @Date: Created in 11:12 2018/6/11
     */
    public class RepaymentHelperTest extends FundApplicationTests {
    
        @Autowired
        RepaymentHelper repaymentHelper;
        @Autowired
        private RouterFeign routerFeign;
    
        @Test
        public void hystrixTest() throws InterruptedException {
    
            for (int i = 0; i < 135; i++) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        job();
                    }
                }).start();
            }
    
            Thread.currentThread().join();
        }
    
        public void job() {
            String repaymentNo = "xf1002";
            String transNo = "T4324324234";
            String reqNo = "xf1002";
            String begintime = "20180831130030";
            String endtime = "20180831130050";
    
            TransRecQueryReqDto transRecQueryReqDto = new TransRecQueryReqDto();
            transRecQueryReqDto.setTransNo(transNo);
            transRecQueryReqDto.setBeginTime(begintime);
            transRecQueryReqDto.setEndTime(endtime);
            transRecQueryReqDto.setReqNo(reqNo);
    
            Resp<List<TransRecDto>> queryTransRecListResp = routerFeign.queryTransRec(new Req<>(repaymentNo, "2018080200000002", null, null, transRecQueryReqDto));
    
            System.out.println(String.format("获取结果为:【%s】", JsonUtil.toJson(queryTransRecListResp)));
        }
    }
    
    • 这个测试类的作用就是创建135个线程,通过RouterFeign类并发请求B服务方,看看请求结果是否出现异常。

    Feign调用代码:

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

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

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

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

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

    image

    • 左窗口是B服务方,右窗口是A调用方。从结果可以看出,调用135次,成功32次左右,其余线程全部抛异常。
    案例二(将核心线程数调低,最大队列数调小一点,但是队列拒绝阈值设置大一点):
    Copy
    hystrix:
      threadpool:
        default:
          coreSize: 10
          maxQueueSize: 15
          queueSizeRejectionThreshold: 2000
    
    此时的结果:
    Copy
    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]
    

    image

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

    image

    • 左窗口是B服务方,右窗口是A调用方。此时的结果和案例一的情况一样,调用135次,成功47次左右,其余线程全部抛异常。报错跟案例一一样
    案例四(将核心线程数调低,最大队列数不设值,但是队列拒绝阈值设置的比较大):
    Copy
    hystrix:
      threadpool:
        default:
          coreSize: 10
          queueSizeRejectionThreshold: 1000
    
    此时的结果:
    Copy
    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]
    	at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)
    	at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)
    	at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)
    	at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:112)
    

    image

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

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

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

    image

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

    Spring Hystrix 官方文档

  • 相关阅读:
    CodeForces 347B Fixed Points (水题)
    CodeForces 347A Difference Row (水题)
    CodeForces 346A Alice and Bob (数学最大公约数)
    CodeForces 474C Captain Marmot (数学,旋转,暴力)
    CodeForces 474B Worms (水题,二分)
    CodeForces 474A Keyboard (水题)
    压力测试学习(一)
    算法学习(一)五个常用算法概念了解
    C#语言规范
    异常System.Threading.Thread.AbortInternal
  • 原文地址:https://www.cnblogs.com/shihaiming/p/13375643.html
Copyright © 2011-2022 走看看