Servlet Async 異步調用處理方式

異步調用

異步調用前,咱們說說它對應的同步調用。一般開發過程當中,通常上咱們都是同步調用,即:程序按定義的順序依次執行的過程,每一行代碼執行過程必須等待上一行代碼執行完畢後才執行。而異步調用指:程序在執行時,無需等待執行的返回值可繼續執行後面的代碼。顯而易見,同步有依賴相關性,而異步沒有,因此異步可併發執行,可提升執行效率,在相同的時間作更多的事情。html

回調:處理異步同步外,還有一個叫回調。其主要是解決異步方法執行結果的處理方法,好比在但願異步調用結束時返回執行結果,這個時候就能夠考慮使用回調機制。前端

原生異步請求API說明

在編寫實際代碼以前,咱們來了解下一些關於異步請求的api的調用說明。java

  • 獲取AsyncContext:根據HttpServletRequest對象獲取。
1
AsyncContext asyncContext = request.startAsync();
  • 設置監聽器:可設置其開始、完成、異常、超時等事件的回調處理

其監聽器的接口代碼:web

1
2
3
4
5
6
public interface AsyncListener extends EventListener {
     void onComplete(AsyncEvent event) throws IOException;
     void onTimeout(AsyncEvent event) throws IOException;
     void onError(AsyncEvent event) throws IOException;
     void onStartAsync(AsyncEvent event) throws IOException;
}

說明:spring

  1. onStartAsync:異步線程開始時調用
  2. onError:異步線程出錯時調用
  3. onTimeout:異步線程執行超時調用
  4. onComplete:異步執行完畢時調用

通常上,咱們在超時或者異常時,會返回給前端相應的提示,好比說超時了,請再次請求等等,根據各業務進行自定義返回。同時,在異步調用完成時,通常須要執行一些清理工做或者其餘相關操做。api

須要注意的是隻有在調用request.startAsync前將監聽器添加到AsyncContext,監聽器的onStartAsync方法纔會起做用,而調用startAsyncAsyncContext還不存在,因此第一次調用startAsync是不會被監聽器中的onStartAsync方法捕獲的,只有在超時後又從新開始的狀況下onStartAsync方法纔會起做用。併發

  • 設置超時:經過setTimeout方法設置,單位:毫秒。

必定要設置超時時間,不能無限等待下去,否則和正常的請求就同樣了。。app

 

Servlet方式實現異步請求

package com.example.demo.Servlet;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.AsyncContext;
import javax.servlet.AsyncEvent;
import javax.servlet.AsyncListener;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 使用Servlet方式進行異步請求
 */
@Slf4j
@RestController
public class ServletController {

    @RequestMapping(value = "/servlet/orig")
    public void todo(HttpServletRequest request, HttpServletResponse response) throws InterruptedException, IOException {
        response.setContentType("ext/html;charset=UTF-8");
        Thread.sleep(1000);
        response.getWriter().print("這是【正常】的請求返回");
    }

    @RequestMapping(value = "/servlet/async")
    public void todoAsync(HttpServletRequest request, HttpServletResponse response) {
        AsyncContext asyncContext = request.startAsync();
        asyncContext.addListener(new AsyncListener() {
            @Override
            public void onComplete(AsyncEvent asyncEvent) throws IOException {
                log.info("執行完成");
            }

            @Override
            public void onTimeout(AsyncEvent asyncEvent) throws IOException {
                log.info("超時了");
            }

            @Override
            public void onError(AsyncEvent asyncEvent) throws IOException {
                log.info("發生錯誤");
            }

            @Override
            public void onStartAsync(AsyncEvent asyncEvent) throws IOException {
                log.info("線程開始");
            }
        });
        asyncContext.setTimeout(20000);
        asyncContext.start(() -> {
            try {
                Thread.sleep(10000);
                System.out.println("ddddd");
                log.info("內部線程:"+Thread.currentThread().getName());
                asyncContext.getResponse().setCharacterEncoding("utf-8");
                asyncContext.getResponse().setContentType("text/html;charset=UTF-8");
                asyncContext.getResponse().getWriter().print("這是【異步】的請求返回");

            }catch (Exception e){
                log.error("異常",e);
            }
            //異步請求完成通知
            //此時整個請求才完成
            //其實能夠利用此特性 進行多條消息的推送 把鏈接掛起。。
            asyncContext.complete();
        });
        System.out.println("main方法線程");
        //此時之類 request的線程鏈接已經釋放了
        log.info("線程:" + Thread.currentThread().getName());
    }
}

注意:異步請求時,能夠利用ThreadPoolExecutor自定義個線程池。異步

1.啓動下應用,查看控制檯輸出就能夠獲悉是否在同一個線程裏面了。同時,可設置下等待時間,以後就會調用超時回調方法了。你們可本身試試。async

相關文章
相關標籤/搜索