目前的工作场景是:
在一个项目中需要调用外部接口,此接口一次只能处理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属性的配置,一般线程被回收前的存活时间应该小于最大超时时间,即在请求时间超出超时时间之前,线程应该都处于存活,并处理完所有的请求。
---------------------
原文:https://blog.csdn.net/nb7474/article/details/84440822