SpringBoot性能優化-異步響應

轉自:https://www.jianshu.com/p/b9c17a44bd6e

業務開發中,有很多場景會有比較耗時的操作
比如需要調用第三方接口查詢數據、發郵件等
都有可能需要1秒以上的時間
如果按照傳統的方式處理,即是線程阻塞1秒以上的時間去等待結果,再把結果返回給用戶
而且處理請求的線程池中線程數總是有限的,如果線程都阻塞在等待中,後續的請求只能排隊等候
這也就影響到了服務器的併發處理能力
爲了讓請求的線程儘早的釋放出來,就需要使用異步方式處理耗時的請求
簡單的思路就是當有耗時操作時,讓請求的線程先退出,把任務交給另一個線程來處理,
這樣請求的線程就可以繼續處理後面的請求,提高了服務器的併發處理能力

Servlet API3.0就增加了異步處理方法,考慮到使用寫底層servlet的機會比較少,主流的開發還是基於springboot 或springmvc
本文主要解釋一下springboot的異步處理方法

方法一:WebAsyncTask

創建一個Callable對象,並由新線程去執行耗時任務,並由新線程返回response給用戶

@RestController
public class GreetingController {
 
 
    @RequestMapping("/greeting")
    public  WebAsyncTask<byte[]> greeting(@RequestParam(value="name", defaultValue="World") String name) {
 
        Callable<byte[]> callable = new Callable<byte[]>() {
 
            @Override
            public byte[] call() throws Exception {
                // TODO Auto-generated method stub
                try {
                    //等待三秒,模擬耗時或阻塞操作
                    Thread.sleep(3000);
                    System.out.println("業務處理線程方法執行完畢時間 : "+System.currentTimeMillis()+"秒");
                    byte[] bs = "123456".getBytes();
                    return bs;
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                return null;
            }
        };
 
 
        System.out.println("請求處理線程方法執行完畢時間  : "+System.currentTimeMillis()+"秒");
 
        return new WebAsyncTask<byte[]>(callable);
    }
}

方法二:DefferedResult

創建一個DefferedResult 對象,把這個對象寄存起來,可以創建一個新線程來處理耗時任務,也可以交給其它線程處理,比如下面的代碼,就是交給一個定時任務來處理,你也可以把這個對象交給隊列的訂閱事件來處理,這樣就是一個完全不阻塞的流程了。

@RestController
public class TestDelay{
 
    private static Map<String,DeferredResult<String>> deferreds=new ConcurrentHashMap<>();
    
 
    @RequestMapping("testDeferred")
    public DeferredResult<String> testDeferred(@RequestParam("msgId") String msgId) {
        DeferredResult<String> deferredResult = new DeferredResult<>();
        deferreds.put(msgId, deferredResult);
        return deferredResult;
    }
    /**
     * 定時任務
     */
    @Scheduled(fixedDelay = 5000)
    public void taskResp() {
        logger.debug(deferreds);
        if(deferreds!=null && deferreds.size()>0) {
            deferreds.forEach((k,deferredResult)->{
                try {
                    deferredResult.setResult(k+"1234");
                    deferreds.remove(k);
                } catch (Exception e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            });
        }
    }
 
}

由上面兩個例子可以看出:
WebAsyncTask 可以釋放請求的主線程,但還是要另一個線程來阻塞處理
DefferedResult 則可以用非阻塞的方法來處理請求
所以在實際場景中,會更多的使用到DefferedResult



作者:愛吐槽的coder
鏈接:https://www.jianshu.com/p/b9c17a44bd6e
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯繫作者獲得授權並註明出處。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章