異步處理

Web容器會為每一個請求分配一個線程,默認(rèn)情況下,響應(yīng)完成前,該線程占用的資源都不會釋放。如果有些請求需要長時間處理(例如長時間運算,等待某個資源),就會長時間占用線程所需資源,對系統(tǒng)的性能造成負(fù)擔(dān)
Servlet3.0新增了異步處理,可以先釋放容器分配給請求的線程和相關(guān)資源,原先釋放了容器所分配線程的請求,響應(yīng)將會被延后
為了支持異步處理,ServletRequest中提供了startAsync()方法,方法的處理都是返回AsyncContext對象
public interface AsyncContext {
    String ASYNC_REQUEST_URI = "javax.servlet.async.request_uri";
    String ASYNC_CONTEXT_PATH = "javax.servlet.async.context_path";
    String ASYNC_PATH_INFO = "javax.servlet.async.path_info";
    String ASYNC_SERVLET_PATH = "javax.servlet.async.servlet_path";
    String ASYNC_QUERY_STRING = "javax.servlet.async.query_string";

    ServletRequest getRequest();

    ServletResponse getResponse();

    boolean hasOriginalRequestAndResponse();

    void dispatch();

    void dispatch(String var1);

    void dispatch(ServletContext var1, String var2);

    void complete();

    void start(Runnable var1);

    void addListener(AsyncListener var1);

    void addListener(AsyncListener var1, ServletRequest var2, ServletResponse var3);

    <T extends AsyncListener> T createListener(Class<T> var1) throws ServletException;

    void setTimeout(long var1);

    long getTimeout();
}
如果要調(diào)用ServletRequest的startAsync()以取得AsyncContext,必須告知容器當(dāng)前Servlet支持異步處理,可以在@WebServlet注解中設(shè)置asyncSupported為ture
@WebServlet(name = "AsyncServlet",urlPatterns = "async",asyncSupported = true)
當(dāng)然可以在web.xml中設(shè)置,如下:
<servlet>
    <servlet-name>AsyncServlet</servlet-name>
    <servlet-class>AsyncServlet</servlet-class>
    <!-- 設(shè)置當(dāng)前Servlet支持異步處理 -->
    <async-supported>true</async-supported>
</servlet>
<servlet-mapping>
    <servlet-name>AsyncServlet</servlet-name>
    <url-pattern>/async</url-pattern>
</servlet-mapping>
如果當(dāng)前支持異步處理的Servlet之前有過濾器,則過濾器也需要設(shè)置asyncSupported為true,同樣可以在注解或web.xml中設(shè)置,否則會拋出如下錯誤:
A filter or servlet of the current chain does not support asynchronous operations.
@WebServlet(name = "AsyncServlet", urlPatterns = "/async", asyncSupported = true)
public class AsyncServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        // 1、開啟異步處理,響應(yīng)延后
        AsyncContext async = request.startAsync();

        // 2、做一些長時間運算或等待某個資源的操作

        // 3、結(jié)束異步處理,開始對客戶端響應(yīng)
        async.complete();
    }
}

模擬服務(wù)器推送消息

Http是基于請求和響應(yīng)規(guī)范,Http服務(wù)器無法直接對客戶端傳輸消息,因為沒有請求就沒有響應(yīng);在這種請求、響應(yīng)模型下,如果客戶端想要獲取服務(wù)器端的最新狀態(tài),必須以定期方式發(fā)送請求,查詢服務(wù)器端的最新狀態(tài),但這種方式會浪費網(wǎng)絡(luò)流量。
一種解決方案就是,服務(wù)器將每次請求都延后處理,直到服務(wù)器端數(shù)據(jù)發(fā)送變化之后在進(jìn)行響應(yīng),當(dāng)然這樣的話客戶端會一直處于等待響應(yīng)狀態(tài),不過可以搭配Ajax發(fā)送異步請求,對請求在延后響應(yīng),就OK了。這就是所謂的服務(wù)器推送機(jī)制
在web容器中,我們可以監(jiān)聽所有的異步請求,然后統(tǒng)一做響應(yīng)延后處理,實現(xiàn)服務(wù)器推送,代碼如下:
/**
 * 監(jiān)聽所有的異步請求,實現(xiàn)服務(wù)器推送的目的
 */
@WebListener()
public class AsyncsListener implements ServletContextListener {

    // 所有的異步請求都放在該集合中
    private List<AsyncContext> asyncs = new ArrayList<>();

    @Override
    public void contextInitialized(ServletContextEvent event) {
        event.getServletContext().setAttribute("asyncs", asyncs);
        new Thread(new Runnable() {
            @Override
            public void run() {
                int num = (int) (Math.random() * 1000);
                try {
                    Thread.sleep(num);
                    synchronized (asyncs) {
                        for (AsyncContext async : asyncs) {
                            async.getResponse().getWriter().print(num);
                            async.complete();
                        }
                        asyncs.clear();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();

    }

    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {

    }
}

更多的AsyncContext細(xì)節(jié)

1. 如果沒有聲明 asyncSupported為true, 調(diào)用startAsync()將會拋出IllegalStateException
2. 當(dāng)在支持異步處理的Servlet中調(diào)用startAsync()方法時,該次請求會離開容器所分配的線程
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容