### 返回Callable
Spring MVC 3.2开始引入了基于Servlet 3的异步请求处理。相比以前,控制器方法已经不一定需要返回一个值,而是可以返回一个java.util.concurrent.Callable的对象,并通过Spring MVC所管理的线程来产生返回值
```
@Component
public class TaskService {
public String execute() {
try {
TimeUnit.SECONDS.sleep(15);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "hello";
}
}
@RestController
public class AsyncCallableController {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private final TaskService taskService;
@Autowired
public AsyncCallableController(TaskService taskService) {
this.taskService = taskService;
}
@RequestMapping(value = "/callable", method = RequestMethod.GET, produces = "text/html")
public Callable<String> executeSlowTask() {
logger.info("Request received");
Callable<String> callable = taskService::execute;
logger.info("Servlet thread released");
return callable;
}
}
output===>
Request received
Servlet thread released
Slow task executed
```
Callable的异步请求被处理时所依次发生的事件:
1. 控制器先返回一个Callable对象
2. Spring MVC开始进行异步处理,并把该Callable对象提交给另一个独立线程的执行器TaskExecutor处理
3. DispatcherServlet和所有过滤器都退出Servlet容器线程,但此时方法的响应对象仍未返回
4. Callable对象最终产生一个返回结果,此时Spring MVC会重新把请求分派回Servlet容器,恢复处理
5. DispatcherServlet再次被调用,恢复对Callable异步处理所返回结果的处理
### 返回DeferredResult
**DeferedResult处理流程**
1. Spring mvc的控制层接收用户的请求之后,如果要采用异步处理,那么就要返回DeferedResult<>泛型对象。在调用完控制层之后,立即回返回DeferedResult对象,此时驱动控制层的容器主线程,可以处理更多的请求
2. 可以将DeferedResult对象作为真实响应数据的代理,而真实的数据是该对象的成员变量result,它可以是String类型,或者ModelAndView类型等
3. 容器主线程,会调用DeferedResult对象的getResult方法,然后响应到客户端。在业务没有处理完毕时,result真实数据还没有形成,那么容器主线程会发生阻塞
4. 业务处理完毕之后,要执行setResult方法,将真实的响应数据赋值到DeferedResult对象中。此时,异步线程会唤醒容器主线程。那么容器主线程会继续执行getResult方法,将真实数据响应到客户端
```
@RestController
public class AsyncDeferredController {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private final TaskService taskService;
@Autowired
public AsyncDeferredController(TaskService taskService) {
this.taskService = taskService;
}
@RequestMapping(value = "/deferred", method = RequestMethod.GET, produces = "text/html")
public DeferredResult<String> executeSlowTask() {
logger.info("Request received");
DeferredResult<String> deferredResult = new DeferredResult<>();
CompletableFuture.supplyAsync(taskService::execute)
.whenCompleteAsync((result, throwable) -> deferredResult.setResult(result));
logger.info("Servlet thread released");
return deferredResult;
}
}
```
> 在Spring Mvc的控制层中,只要有一个用户请求便会实例化一个DeferedResult对象,然后返回该对象,进行响应客户端。只要DeferedResult对象不设置result响应的内容,则控制层的容器主线程在响应客户端上就会发生阻塞。因为SpringMVC只会实例化一个Controller对象,无论有多少个用户请求,在堆上只有一个Controller对象,因此可以添加一个成员变量List,将这些用户请求的DeferedResult对象存放到List中,然后启动一个定时线程扫描list,从而依次执行setResult方法,响应客户端
```
@Controller
public class DeferedResultController {
private ConcurrentLinkedDeque<DeferredResult<String>> deferredResults =
new ConcurrentLinkedDeque<DeferredResult<String>>();
@RequestMapping("/getResult")
@ResponseBody
public DeferredResult<String> getDeferredResultController(){
//设置 5秒就会超时
final DeferredResult<String> stringDeferredResult = new DeferredResult<String>(1000);
//将请求加入到队列中
deferredResults.add(stringDeferredResult);
final String message = "{username:wangbinghua}";
ExecutorService executorService = Executors.newFixedThreadPool(10);
executorService.submit(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1010);
} catch (InterruptedException e) {
e.printStackTrace();
}
//业务处理
System.out.println("业务处理");
stringDeferredResult.setResult(message);
}
});
//setResult完毕之后,调用该方法
stringDeferredResult.onCompletion(new Runnable() {
@Override
public void run() {
System.out.println("异步调用完成");
//响应完毕之后,将请求从队列中去除掉
deferredResults.remove(stringDeferredResult);
}
});
stringDeferredResult.onTimeout(new Runnable() {
@Override
public void run() {
System.out.println("业务处理超时");
stringDeferredResult.setResult("error:timeOut");
}
});
return stringDeferredResult;
}
//开启线程定时扫描队列,响应客户端
@Scheduled(fixedRate = 1000)
public void scheduleResult(){
System.out.println(new Date());
for(int i = 0;i < deferredResults.size();i++){
DeferredResult<String> deferredResult = deferredResults.getFirst();
deferredResult.setResult("result:" + i);
}
}
}
```
### DeferedResult 两个监听器(onCompletion & onTimeout)
* 当DeferedResult对象调用setResult之后,响应完毕客户端,则直接调用onCompletion对应的方法。
* 当业务处理相当耗时,则响应客户端超时,也会调用onCompletion对应的方法以及onTimeout方法。此时,响应客户端的内容为deferedResult.setErrorResult的内容,否则500错误。
* 发生异常,调用onCompletion方法,此时,响应客户端的内容为deferedResult.setErrorResult的内容,否则500错误
### WebAsyncTask对象使用实例
```
@RequestMapping("/async")
@ResponseBody
public WebAsyncTask<String> asyncTask(){
// 1000 为超时设置
WebAsyncTask<String> webAsyncTask = new WebAsyncTask<String>(1000,new Callable<String>(){
@Override
public String call() throws Exception {
//业务逻辑处理
Thread.sleep(5000);
String message = "username:wangbinghua";
return message;
}
});
webAsyncTask.onCompletion(new Runnable() {
@Override
public void run() {
System.out.println("调用完成");
}
});
webAsyncTask.onTimeout(new Callable<String>() {
@Override
public String call() throws Exception {
System.out.println("业务处理超时");
return "<h1>Time Out</h1>";
}
});
return webAsyncTask;
}
```
### 总结
Callable和Deferredresult做的是同样的事情——释放容器线程,在另一个线程上异步运行长时间的任务。不同的是谁管理执行任务的线程:Callable执行线程完毕即返回;Deferredresult通过设置返回对象值(deferredResult.setResult\(result\)\);)返回,可以在任何地方控制返回
- 概述
- 网络时延
- 进程间通信
- URI
- URL
- URN
- NAT
- 操作系统基础
- 内核
- 用户空间
- 网络协议模型
- 四层网络协议模型
- 链路层
- 以太网协议
- ARP协议
- RARP协议
- MAC地址
- 网络层
- IP协议
- ICMP协议
- 子网掩码
- 传输层
- TCP协议
- TCP慢启动
- TCP性能
- UDP协议
- SCTP协议
- 应用层
- DNS
- TCP/IP协议族
- Socket
- Socket通信模型
- socket和TCP/IP协议族
- Socket三次握手四次挥手
- OSI七层模型
- 物理层
- 数据链路层
- 网络层
- 传输层
- 应用层
- HTTP
- 基础
- HTTP/1.0
- HTTP/1.1
- http2.0
- HTTP报文
- WEB浏览器工作机制
- HTTP事务时延
- HTTP与HTTPS区别
- 持久连接
- 用户验证
- web结构组件
- 代理
- 正向代理
- 反向代理
- 缓存
- 网关
- 隧道-tunnel
- Agent代理
- http协议补充
- Servlet3异步请求
- ajax
- Comet
- WebSocket
- SPDY协议
- HTTP/2
- QUIC
- WebDAV
- http方法
- http连接
- 短连接&长连接
- 管线化
- 网络会话
- cookie
- session
- token
- jwt
- cookie与session的区别
- Spring Session
- 分布式session实现方案
- 同源策略
- 跨域
- CORS
- HTTP三大安全问题
- JWT vs OAuth
- HTTPS
- SSL&TLS
- OpenSSL
- HTTPS和TLS/SSL的关系
- X509标准和PKI
- IO模型
- IO
- I/O模型
- 传统阻塞式I/O
- 非阻塞式I/O
- IO复用
- Connection Per Thread模式
- IO多路复用模型流程
- Reactor模式
- 单Reactor单线程
- 单Reactor多线程
- 主从Reactor多线程
- Proactor模型
- Selector模型
- 信号驱动I/O
- 异步I/O
- select/poll/epoll
- select
- poll
- epoll
- select/poll/epoll适用场景
- 零拷贝原理
- 读取文件发送网络内存拷贝
- 零拷贝
- Netty零拷贝
- 密码学
- 密码学Hash算法分类
- 加密算法
- 对称加密
- 非对称加密
- 数字签名
- RSA数字签名算法
- DSA数字签名算法
- 数字证书
- MAC算法
- web安全
- CSRF攻击
- XSS
- cookie劫持
- SQL注入
- DDos攻击
- 常见面试题
- 浏览器工作机制和原理
- XSS如何预防
- 如何防止cookie被劫持
- 附录
- HTTP状态码
- 常用的网络端口