一、异步请求
可以先释放容器分配给请求的线程与相关资源,减轻系统负担,释放了容器所分配线程的请求,其响应将被延后,可以在耗时处理完成(例如长时间的运算)时再对客户端进行响应。就是增加了服务器对客户端请求的吞吐量。
二、异步实现
1.Servlet方式实现异步请求
// curl -X GET "http://localhost:8080/servletReq"
// 执行 controller 方法前,ServletResponseMethodArgumentResolver 会处理 HttpServletResponse 参数,
// 并设置 mavContainer.setRequestHandled(true);
// 使得处理器不会查找 view,渲染 view
@GetMapping("/servletReq")
public void servletReq(HttpServletRequest request, HttpServletResponse response) {
System.out.println("main threadName:" + Thread.currentThread().getName());
AsyncContext asyncContext = request.startAsync();
// 设置监听器:可设置其开始、完成、异常、超时等事件的回调处理
asyncContext.addListener(new AsyncListener() {
@Override
public void onTimeout(AsyncEvent event) throws IOException {
System.out.println(String.format("work threadName:%s. timeout", Thread.currentThread().getName()));
}
@Override
public void onStartAsync(AsyncEvent event) throws IOException {
System.out.println(String.format("work threadName:%s. startAsync", Thread.currentThread().getName()));
}
@Override
public void onError(AsyncEvent event) throws IOException {
System.out.println(String.format("work threadName:%s. error", Thread.currentThread().getName()));
}
@Override
public void onComplete(AsyncEvent event) throws IOException {
System.out.println(String.format("work threadName:%s. complete", Thread.currentThread().getName()));
}
});
// 设置超时时间 20s
asyncContext.setTimeout(20000);
asyncContext.start(new Runnable() {
@Override
public void run() {
try {
System.out.println(String.format("work threadName:%s. success", Thread.currentThread().getName()));
PrintWriter writer = asyncContext.getResponse().getWriter();
for (int i = 0; i < 3; i++) {
TimeUnit.SECONDS.sleep(1);
writer.println("this is data");
writer.flush();
}
} catch (Exception e) {
System.err.println(e.getMessage());
}
// 异步请求完成通知
// 此时整个请求才完成
asyncContext.complete();
}
});
}
结果:
main threadName:http-nio-8080-exec-1
work threadName:http-nio-8080-exec-2. success
work threadName:http-nio-8080-exec-3. complete
2.返回的 callable
// curl -X GET "http://localhost:8080/callableReq"
@GetMapping("/callableReq")
@ResponseBody
// CallableMethodReturnValueHandler 会处理返回值
// 使用 WebAsyncManager 中的 taskExecutor 执行线程
// taskExecutor 默认是 springboot 定义的 TaskExecutionAutoConfiguration#applicationTaskExecutor
// 可使用实现 WebMvcConfigurer#configureAsyncSupport,进而修改 WebAsyncManager 中的 taskExecutor
public Callable<String> callableReq() {
System.out.println("main threadName:" + Thread.currentThread().getName());
Callable<String> callable = () -> {
TimeUnit.SECONDS.sleep(2);
System.out.println(String.format("work threadName:%s. success", Thread.currentThread().getName()));
return "callable!";
};
return callable;
}
@Configuration(proxyBeanMethods = false)
public class RequestAsyncPoolConfigurer implements WebMvcConfigurer {
private static ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
static {
threadPoolTaskExecutor.setCorePoolSize(5);
threadPoolTaskExecutor.setMaxPoolSize(10);
threadPoolTaskExecutor.setThreadNamePrefix("my-callable-req-task-");
threadPoolTaskExecutor.initialize();
}
@Override
public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
// 处理 callable 超时
configurer.setDefaultTimeout(5 * 1000);
configurer.setTaskExecutor(threadPoolTaskExecutor);
}
}
结果:
main threadName:http-nio-8080-exec-1
work threadName:my-callable-req-task-1. success
3.返回 WebAsyncTask
private static ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
static {
threadPoolTaskExecutor.setCorePoolSize(5);
threadPoolTaskExecutor.setMaxPoolSize(10);
threadPoolTaskExecutor.setThreadNamePrefix("my-web-async-req-task-");
threadPoolTaskExecutor.initialize();
}
// curl -X GET "http://localhost:8080/webAsyncReq"
@GetMapping("webAsyncReq")
@ResponseBody
// AsyncTaskMethodReturnValueHandler 会处理返回值
public WebAsyncTask<String> webAsyncReq() {
System.out.println("main threadName::" + Thread.currentThread().getName());
Callable<String> result = () -> {
TimeUnit.SECONDS.sleep(2);
System.out.println(String.format("work threadName:%s. success", Thread.currentThread().getName()));
return "success";
};
// WebAsyncTask<String> task = new WebAsyncTask<>(3000L, result);
WebAsyncTask<String> task = new WebAsyncTask<>(5000L, threadPoolTaskExecutor, result);
task.onTimeout(() -> {
System.out.println(String.format("work threadName:%s. timeout", Thread.currentThread().getName()));
return "timeout";
});
return task;
}
结果:
main threadName::http-nio-8080-exec-1
work threadName:my-web-async-req-task-1. success
4.返回 DeferredResult
// curl -X GET "http://localhost:8080/deferredResultReq"
@GetMapping("/deferredResultReq")
@ResponseBody
// DeferredResultMethodReturnValueHandler 会处理返回值
public DeferredResult<String> deferredResultReq() {
System.out.println("main threadName::" + Thread.currentThread().getName());
// 设置超时时间
DeferredResult<String> result = new DeferredResult<String>(5 * 1000L);
// 处理超时事件 采用委托机制
result.onTimeout(() -> {
System.out.println(String.format("work threadName:%s. timeout", Thread.currentThread().getName()));
result.setResult("timeout!");
});
result.onCompletion(() -> {
System.out.println(String.format("work threadName:%s. complete", Thread.currentThread().getName()));
System.out.println("complete!");
});
new Thread(() -> {
// 处理业务逻辑
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(String.format("work threadName:%s. success", Thread.currentThread().getName()));
// 返回结果
result.setResult("success!");
}).start();
return result;
}
结果:
main threadName::http-nio-8080-exec-2
work threadName:Thread-6. success
work threadName:http-nio-8080-exec-1. complete