Hystrix中threadPoolProperties线程池各个属性举例测试

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/nb7474/article/details/84440822

目前的工作场景是:

在一个项目中需要调用外部接口,此接口一次只能处理8个请求,多于8个请求过来,nginx会为了保护接口直接踢回请求(返回500null错误),而在本项目中使用了消息队列机制,所以有可能会一次从消息队列中消费多条数据,这时候就会有个别请求还没有调用外部接口直接返回了500错误。

这时候就需要考虑对项目中调用接口的方法进行核心线程控制,这就涉及到hystrix的核心线程数概念。

编写代码模拟外部接口

这是一个service方法:

@HystrixCommand(commandKey = "testCoreSizeCommand",groupKey = "testGroup",fallbackMethod = "TimeOutFallBack",
    threadPoolProperties = {
            @HystrixProperty(name = "coreSize",value = "2"),
            @HystrixProperty(name = "allowMaximumSizeToDivergeFromCoreSize",value="true"),
            @HystrixProperty(name = "maximumSize",value="2"),
            @HystrixProperty(name = "maxQueueSize",value="2")
    },
    commandProperties = {
            @HystrixProperty(name = "execution.isolation.strategy",value = "THREAD"),
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "16000" )
    })
    @Override
    public String coreSizeTest() throws InterruptedException {

        Thread.sleep(5000);
        
        System.out.println("进来一次");
        
        return "coreSizeTest finished";
    }
    
    public String TimeOutFallBack(){
        
        return "降级sorry, the request is timeout";
    }

为了更直观的感受,每个请求会sleep 5秒之后返回。

编写一个controller,调用service:

@RequestMapping(value="test_coresize",method=RequestMethod.GET)
    public String getHystrixSizeTest() throws InterruptedException{
        
        String test = hystrixService.coreSizeTest();
        
        return test;
    }

这样通过发送请求访问localhost:8080/test_coresize就可以对接口进行调用。

三个线程同时访问接口

public class MyThred extends Thread{

    @Override
    public void run() {

        RestTemplate restTemplate = new RestTemplate();

        HttpHeaders headers = new HttpHeaders();

        headers.add("Content-Type", "application/json; charset=UTF-8");
        headers.add("Accept", "*/*");

        HttpEntity<String> requestEntity = new HttpEntity<>("", headers);

        ResponseEntity<String> exchange = restTemplate.exchange("http://localhost:8080/test_coresize", HttpMethod.GET, requestEntity,String.class);

        String body = exchange.getBody();
        
        System.out.println("请求结果:"+body);
        
    }
}

public static void main(String[] args) {
        
        MyThred myThred1 = new MyThred();
        MyThred myThred2 = new MyThred();
        MyThred myThred3 = new MyThred();
        myThred1.start();
        myThred2.start();
        myThred3.start();
    }

测试过程中各种情况分析

1. 设置coresize=2,maximumSize=2,未定义降级方法

coresize即核心线程数,即可以同时接受的请求数,maximumSize最大线程数,即超过核心线程数之后,多余的线程会处于空闲状态,等核心线程使用它。开启maximumSize需要设置allowMaximumSizeToDivergeFromCoreSize为true。

这里我设置两个属性都是2,即一次只能接收两个请求,我最初的想法是既然只能接受两个请求,那我发送三个请求,那是不是应该第三个请求等待2个请求处理完了之后再进行处理。一次请求需要5秒时间,同时可以处理两个请求,那么三个请求应该是10s后全部返回给我。

但是现实很快就给了我狠狠的一巴掌,两个请求成功,第三个请求返回错误500 null。原来:超过了最大线程数之后的请求会被hystrix降级处理,即调用hystrxi中的fallback属性指定的降级方法。如果没有指定降级方法,则会默认返回null。这就是为什么我会有一个请求返回了500的错误。

2. 设置coresize=2,maximumSize=2,定义降级方法:返回信息降级sorry, the request is timeout

在定义了降级方法之后,超过最大线程的请求会调用降级方法,这时候就没有出现报错,而是打印了降级方法返回的信息。

3. 设置coresize=2,maximumSize=4,未定义降级方法

由上面的例子我们知道,线程池内核心线程数目都在忙碌,再有新的请求到达时,线程池容量可以被扩充为到最大数量,等到线程池空闲后,多于核心数量的线程还会被回收。

那我设置了maximumSize=4,这时候有3个请求过来的时候,线程池会被扩大到最大数量,最大容量数大于请求数,不会调用降级方法。这时候比较疑惑的点是三个请求一起处理,还是先处理两个,再处理第三个呢。

从测试结果上看:三个请求是一起返回过来的。那这就说明:请求数小于最大线程数,却大于核心线程数的时候,会一起处理所有的请求,当所有请求处理完毕的时候,会将多余核心数量的线程释放。

4. 设置coresize=2,maxQueueSize=2,maximumSize=4,未定义降级方法

maxQueueSize指的是作业队列的最大值,核心线程池内的线程都在忙碌时,会将作业暂时存放在此队列内,但超出此队列的请求依然会被拒绝。默认值为 -1,设置为此值时,队列会使用 SynchronousQueue,此时其 size 为0,Hystrix 不会向队列内存放作业。即默认hystrix是不会使用队列的。

那我现在配置了maxQueueSize=2,那么我发送三个请求的时候,就应该是有2个请求在处理,有一个请求在作业队列中等待处理。最后请求返回的时间也应该不是同步的。果然,有两个请求先返回回来,还有一个请求在5秒后返回。

测试结果总结

  • 如果请求量少,达不到 coreSize,通常会使用核心线程来执行任务。

  • 如果设置了 maxQueueSize,当请求数超过了 coreSize, 通常会把请求放到 queue 里,待核心线程有空闲时消费。

  • 如果 queue 长度无法存储请求,则会创建新线程执行直到达到 maximumSize 最大线程数,多出核心线程数的线程会在空闲时回收。

所以正确的配置方式是根据显示场景需要进行设置:coresize<maxQueueSize<maximumSize

需要注意的是threadPoolProperties还有两个属性:

  • queueSizeRejectionThreshold:由于 maxQueueSize 值在线程池被创建后就固定了大小,如果需要动态修改队列长度的话可以设置此值,即使队列未满,队列内作业达到此值时同样会拒绝请求。此值默认是 5,所以有时候只设置了 maxQueueSize 也不会起作用。

  • keepAliveTimeMinutes:由上面的 maximumSize,我们知道,线程池内核心线程数目都在忙碌,再有新的请求到达时,线程池容量可以被扩充为到最大数量,等到线程池空闲后,多于核心数量的线程还会被回收,此值指定了线程被回收前的存活时间,默认为 2,即两分钟。

在实际的使用过程中,还应该考虑最大超时时间timeoutInMilliseconds与keepAliveTimeMinutes属性的配置,一般线程被回收前的存活时间应该小于最大超时时间,即在请求时间超出超时时间之前,线程应该都处于存活,并处理完所有的请求。

猜你喜欢

转载自blog.csdn.net/nb7474/article/details/84440822
今日推荐