Spring MVC非同步處理-DeferedResult使用
阿新 • • 發佈:2018-12-06
DeferedResult處理流程
- Spring mvc的控制層接收使用者的請求之後,如果要採用非同步處理,那麼就要返回DeferedResult<>泛型物件。在呼叫完控制層之後,立即回返回DeferedResult物件,此時驅動控制層的容器主執行緒,可以處理更多的請求。
- 可以將DeferedResult物件作為真實響應資料的代理,而真實的資料是該物件的成員變數result,它可以是String型別,或者ModelAndView型別等。
- 容器主執行緒,會呼叫DeferedResult物件的getResult方法,然後響應到客戶端。在業務沒有處理完畢時,result真實資料還沒有形成,那麼容器主執行緒會發生阻塞。
- 業務處理完畢之後,要執行setResult方法,將真實的響應資料賦值到DeferedResult物件中。此時,非同步執行緒會喚醒容器主執行緒。那麼容器主執行緒會繼續執行getResult方法,將真實資料響應到客戶端。
DeferedResult物件.png
也可以有這樣的應用場景。在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錯誤。
@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;
}
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;
}