### 返回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狀態碼
- 常用的網絡端口