小型系统一般都是在controller直接处理接口逻辑,这样占用的是tomcat的工作线程
(1)这个线程是有数的,不能无限大
(2)这个线程同时负责接收处理请求,如果被占满,这无法接收新来的请求,导致并发数很低
这时候要引入异步处理的概念,系统中实例化出另一个线程池,用来处理业务工作。
而controller只用来接收请求,并且将业务工作提交到新的线程池。
这样虽然系统硬件配置并未改变,但能够同一时间接收的请求数变多,并且负责工作的线程池会有工作队列,不会像之前那样将请求拒之门外,直到超时丢失。
当前并不是所有的接口都要这么写,只是对于耗时久的接口单独处理即可。
避免耗时久的接口工作时,占满tomcat工作线程,导致其他正常工作的接口无法正常响应。
具体实例:
新建工作线程池
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;import java.util.concurrent.Executor;
import java.util.concurrent.Executors;@Configuration
public class AsyncConfig {@Bean(name = "asyncExecutor")public Executor asyncExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(200); // 设置核心线程池大小executor.setMaxPoolSize(200); // 设置最大线程池大小executor.setQueueCapacity(300); // 设置队列容量executor.setThreadNamePrefix("async-"); // 设置线程名前缀executor.initialize(); // 初始化线程池return executor;}
}
controller处理
@RestController
@RequestMapping("/async")
public class AsyncController {@Resourceprivate Executor asyncExecutor;@Resourceprivate TestMapper testMapper;@GetMapping("/test")public CompletableFuture<String> asyncResponse(@RequestParam(name="id")String id) {System.out.println(id); //标记controller是不是在接收请求return CompletableFuture.supplyAsync(() -> {/***********************************这里就开始处理任务***************************************/try {Thread.sleep(10000); // 假设需要10秒完成处理}catch (Exception e){}/***********************************这里处理结束,返回结果***************************************/return "成功"+testMapper.getCount();}, asyncExecutor);}}
注意最后的返回类型用CompletableFuture包裹。
修改tomcat配置,可以看到tomcat.threads.max=1,实际上通过测试工具发起100次请求,发现都被接收了,并且在处理。发起200次则部分连接被拒绝,因为tomcat.max-connections设置的是100,这个只是一个限制条件,根据系统的承载能力设置,测试时可以调大。
server:tomcat:max-swallow-size: -1 # 请求大小过滤,单位字节max-connections: 100accept-count: 1threads:max: 1
为了明显异步处理的效果,可以换成如下普通的接口再次测试。
@GetMapping("/test")public String asyncResponse(@RequestParam(name="id")String id) {System.out.println(id);try {Thread.sleep(10000); // 假设需要5秒完成处理}catch (Exception e){}return "成功"+testMapper.getCount();}
就可以发现系统只能一个个处理,其他请求都在排队。