从WEB服务器的响应模式来看,Web服务器为用户的每一次请求都分配了一个处理线程,可以称之为用户线程,也就是说本次请求的所有内容都交由这个线程来处理。如果用户单次请求提交的并行任务较多且业务复杂,单一线程的处理速度就会变慢。
为了提升服务器的响应速度,可以采取异步的模式,在用户线程中新开一个或多个任务线程,让程序并行的处理用户请求,最后将处理结果交由用户线程整合后返回。
Callable接口可以开启新线程执行,并将执行的结果返回主线程,那么可以尝试直接使用Callable做任务的并行处理。
@Slf4j
@RestController
@RequestMapping("/message")
public class MessageAction {
@RequestMapping("/echo")
public Object echo(String message) {
log.info("【用户线程】" + Thread.currentThread().getName());
return new Callable<String>() {
@Override
public String call() throws Exception {
log.info("【任务线程】" + Thread.currentThread().getName());
return "[ECHO]" + message;
}
};
}
}
访问:http://localhost:8080/message/echo,发现用户线程和使用Callable接口的任务线程都是来自线程池中。
配置任务线程的线程池。
/**
* 异步线程配置类
*/
@Configuration
public class AsyncPoolConfig implements WebMvcConfigurer {
@Override
public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
configurer.setDefaultTimeout(1000); //超时时间
configurer.registerCallableInterceptors(timeoutInterceptor()); // timeout拦截器
configurer.setTaskExecutor(getThreadPoolTaskExecutor());
}
@Bean(name = "asyncPoolTaskExecutor")
public ThreadPoolTaskExecutor getThreadPoolTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(20); // 核心线程数
executor.setMaxPoolSize(100); // 最大线程数
executor.setQueueCapacity(25); // 工作队列大小
executor.setKeepAliveSeconds(200); // 线程无任务存活时间
executor.setThreadNamePrefix("async-pool-task-"); // 线程名前缀
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); // 拒绝策略
return executor;
}
@Bean
public TimeoutCallableProcessingInterceptor timeoutInterceptor() {
return new TimeoutCallableProcessingInterceptor();
}
}
WebAsyncTask是一个由Spring提供的异步任务管理类,可以在此类中配置要执行的请求处理异步线程,也可以配置一个与之相关的超时管理线程,当程序超时后,可以启动超时处理线程进行结果的返回。
修改MessageAction接口,配置超时。
@Slf4j
@RestController
@RequestMapping("/message")
public class MessageAction {
@RequestMapping("/echo")
public Object echo(String message) {
log.info("【用户线程】" + Thread.currentThread().getName());
Callable task = new Callable<String>() {
@Override
public String call() throws Exception {
log.info("【任务线程】" + Thread.currentThread().getName());
TimeUnit.SECONDS.sleep(2); // 休眠2s
return "[ECHO]" + message;
}
};
WebAsyncTask webAsyncTask = new WebAsyncTask(200, task); // 设置task超时时间为200ms
// 设置超时处理线程
webAsyncTask.onTimeout(new Callable<String>() {
@Override
public String call() throws Exception {
log.info("【超时线程】" + Thread.currentThread().getName());
return "timeout";
}
});
return webAsyncTask;
}
}
DeferredResult用于实现Runnable接口的异步处理,与Callable接口不同,Runnable接口的run方法是没有返回值的,需要将执行结果保存在DeferredResult对象实例中。DeferredResult中也提供了状态监听方法处理超时。
修改MessageAction接口,使用Runnable接口处理任务。
@Slf4j
@RestController
@RequestMapping("/message")
public class MessageAction {
private final ThreadPoolTaskExecutor threadPoolExecutor; // 线程池
@Autowired
public MessageAction(ThreadPoolTaskExecutor threadPoolExecutor) {
this.threadPoolExecutor = threadPoolExecutor;
}
@RequestMapping("/echo")
public Object echo(HttpServletRequest request, String message) {
log.info("【用户线程】" + Thread.currentThread().getName());
DeferredResult result = new DeferredResult(6000L);
// 监听超时
result.onTimeout(() -> {
log.info("【超时线程】" + Thread.currentThread().getName());
result.setResult("超时路径: " + request.getRequestURI());
});
// 监听完成
result.onCompletion(() -> {
log.info("【完成线程】" + Thread.currentThread().getName());
});
threadPoolExecutor.execute(() -> {
log.info("【任务线程】" + Thread.currentThread().getName());
try {
TimeUnit.SECONDS.sleep(2); // 休眠2s
} catch (InterruptedException e) {
e.printStackTrace();
}
result.setResult("[ECHO]" + message); // 设置Runnable处理后的返回值
});
return result;
}
}
SpringBoot提供有异步任务的概念,在请求接收到之后,响应返回之前开启一个异步任务,该任务与相应的内容无关,并且可能在相应结束后继续执行。
配置异步任务线程池管理类。
@Configuration
@EnableAsync // 启用异步处理
public class DefaultThreadPoolConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() { // 异步执行者
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10); // 核心线程数
executor.setMaxPoolSize(20); // 最大线程数
executor.setQueueCapacity(100); //任务队列大小
executor.setThreadNamePrefix("NicholasGUB - "); // 前缀
executor.initialize();
return executor;
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { // 异常处理
return new SimpleAsyncUncaughtExceptionHandler();
}
}
定义异步任务处理类。
@Component
@Slf4j
public class ThreadTask {
@Async
public void startTaskHandle() {
log.info("【异步线程】开启:" + Thread.currentThread().getName());
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("【异步线程】结束:" + Thread.currentThread().getName());
}
}
开启异步任务。
@Slf4j
@RestController
@RequestMapping("/task")
public class TaskAction {
private final ThreadTask threadTask; // 异步任务
@Autowired
public TaskAction(ThreadTask threadTask) {
this.threadTask = threadTask;
}
@RequestMapping("/start")
public Object echo() {
log.info("【用户线程】" + Thread.currentThread().getName());
threadTask.startTaskHandle(); // 异步任务执行
return "async task start success";
}
}