springmvc 异步请求
1、Servlet 规范
为什么需要异步请求?任何技术产生都会有一定的业务场景需求。假设我们有这样的一种业务场景,用户输入某种操作之后,业务逻辑部分会占用大量的时间去处理,这时候如果是同步的处理方式,servlet 线程被阻塞,直到所有的处理完成。如果服务器的请求得到了很多过程,它将达到最大 servlet 线程限制和进一步的请求会拒绝连接错误。servlet 线程应该只是充当 http 协议的搬运工,从接受到用户请求,将数据运送到一个子线程去处理,立即返回继续接受其他请求,当前请求会由子线程处理后返回响应。以下两种时序图分别对应同步和异步的两种方式。
在servlet3.0 规范里面提供了异步的规范。
title: 同步方式
participant 客户端
participant 控制器
participant 逻辑处理
客户端->控制器: 请求响应
控制器->逻辑处理: 逻辑处理
逻辑处理->控制器: 返回处理结果
控制器->客户端: 返回响应
复制代码
title: 异步方式
participant 客户端
participant 控制器
participant 逻辑处理
客户端->控制器: 请求响应
控制器->客户端:释放 servlet 线程
控制器-->逻辑处理: 逻辑处理
逻辑处理-->控制器: 返回处理结果
控制器-->客户端: 返回响应
note right of 客户端: 子线程返回响应
复制代码
2、springmvc 异步请求
SpringMvc3.2 之后支持异步请求,能够在 controller 中返回一个 Callable 或者 DeferredResult,这样可以提高系统的吞吐量。
- Callable 的时候,处理流程大致为Spring会启动一个线程去将Callable交给TaskExecutor去处理,然后 DispatcherServlet 退出主线程,同时把 Response 保持打开状态,等到 Callable 处理完成,SpringMvc会重新分配一个Request 请求,DispatcherServlet 重新调用和Callable处理的返回结果值,然后返回视图。
代码示例
@GetMapping
public Callable<String> hello() throws Exception {
Callable<String> helloworld = new Callable<String>() {
@Override
public String call() throws Exception {
return "Hello World!";
}
};
return helloworld;
}
复制代码
//java8 可以这样些
@GetMapping
public Callable<String> hello() throws Exception {
return ( () -> "Hello World!");
}
复制代码
- DeferredResult 的时候,执行流程有点区别的是,Callable的时候是由spring去启动的一个线程去执行Callble,而 DeferredResult 的执行线程是由我们(程序员)控制的,只需要将结果set进去即可。
代码示例
//定义执行逻辑的service
class TestService{
public String exec() {
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
LoggerFactory.getLogger(TestService.class).error("TestService");
return "HelloWorld";
}
}
//控制器
@GetMapping
public DeferredResult<String> world() throws Exception{
logger.error("DeferredResult");
DeferredResult<String> deferredResult = new DeferredResult<>();
//异步事件驱动编程模型
CompletableFuture.supplyAsync(new TestService()::exec).whenCompleteAsync((result, throwable) -> deferredResult.setResult(result));
return deferredResult;
}
复制代码
总得来说,不论是Callable异或DeferredResult,都是通过释放容器线程,通过另外一个副线程执行逻辑的形式来扩展系统的吞吐能力。
扩展阅读 java多线程的一点资料