1. 程式人生 > >Spring MVC非同步處理-DeferedResult使用

Spring MVC非同步處理-DeferedResult使用

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;
    }