天天看點

Servlet異步請求

Tomcat實作異步Servlet

有時Servlet在生成響應封包前需要等待某些耗時的操作,例如等待一個可用的JDBC連接配接或等待一個遠端Web服務的響應,是以會導緻Servlet中等待阻塞會導緻web整體處理能力低下,是以對于比較耗時的操作可以放置到另外一個線程中進行處理,此過程保留連接配接的請求和響應對象,在處理完成之後可以把處理的結果通知到用戶端,對于這種情況servlet規範中定義了異步處理方式。

Servlet在同步情況下的處理過程:Tomcat的用戶端請求由管道處理最後會通過Wrapper容器的管道,這時它會調Servlet執行個體的service方法進行邏輯處理,處理完後響應用戶端,整個處理由Tomcat的Executor線程池的線程處理,而線程池的最大線程數使有限制的,是以這個處理過程越短、越快把線程讓回線程池就越好。但如果Servlet中的處理邏輯耗時越長就會導緻長期地占用Tomcat的處理線程池,影響Tomcat的整體性能。

為了解決上面的問題引入了支援異步的Servlet,同樣是用戶端請求到來,然後通過管道最後進入到Wrapper容器的管道,調用Servlet執行個體的service後,建立一個異步上下文(AysncContext)将耗時的邏輯操作封裝起來,交給自己定義的線程池,這時Tomcat的處理線程就能馬上回到Executor線程池,而不用等待耗時的操作完成才讓出線程,進而提升了Tomcat的整體處理能力。這裡要注意的是,由于後面做完耗時的操作後還需要對用戶端響應,是以需要保持住Request和Response對象,以便輸出響應封包到用戶端。

Servlet異步處理

再結合一個簡單的異步代碼來看Tomcat對Servlet異步的實作:

@WebServlet(urlPatterns = “/some.do”, asyncSupported = true)

//想要實作異步的servlet需要添加WebServlet的注解,注解中添加屬性asyncSupported=true

//如果使用web.xml設定Servlet,則可以在中設定async-supported标簽為true

public class AsyncServlet extends HttpServlet {

//自定義線程池

ScheduledThreadPoolExecutor userExecutor = new ScheduledThreadPoolExecutor(2);

public void doGet(HttpServletRequest req, HttpServletResponse res) {
     response.setContentType("text/html; charset=UTF8");
    AsyncContext aCtx = req.startAsync(req, res);//AysncContext
    userExecutor.execute(new AsyncHandler(aCtx));//執行異步處理邏輯

}
           

}

public class AsyncHandler implements Runnable {

private AsyncContext ctx;

public AsyncHandler(AsyncContext ctx) {
    this.ctx = ctx;
}
           

/請求與響應對象都封裝在AsyncContext中,是以AsyncRequest建構時必須接受AsyncContext執行個體。範例中以暫停線程的方式來模拟長時間處理?,并輸出簡單的字元串作為響應文字?,最後調用AsyncContext的complete()對用戶端完成響應。/

@Override

public void run() {

//耗時操作

PrintWriter pw;

try {

Thread.sleep(1000);

pw = ctx.getResponse().getWriter();

pw.print(“done!”);

pw.flush();

pw.close();

} catch (IOException e) {

e.printStackTrace();

}

ctx.complete();

}

}

如果Servlet将會進行異步處理,若其前端有過濾器,則過濾器亦需标示其支援異步處理,如果使用@WebFilter,同樣可以設定其asyncSupported為true。例如:

@WebFilter(urlPatterns = “/some.do”, asyncSupported = true)

public class AsyncFilter implements Filter{

如果使用web.xml設定過濾器,則可以設定async-supported标簽為true:

我們建立一個AsyncServlet,它定義了一個userExecutor線程池專門用于處理該Servlet的所有請求的耗時的邏輯操作。這樣就不會占用Tomcat内部的Executor線程池,影響到對其他Servlet的處理。這種思想有點像資源隔離,耗時的操作統一由指定的線程池處理,而不要影響其它耗時少的請求處理。

Servlet的異步的實作就很好了解了,startAsync方法其實就是建立了一個異步上下文AsyncContext對象,該對象封裝了請求和響應對象。然後建立一個任務用于處理耗時邏輯,後面通過AsyncContext對象獲得響應對象并對用戶端響應,輸出“done!”。将暫緩至調用AsyncContext的complete()或dispatch()方法為止,前者表示響應完成,後者表示将調派指定的URL進行響應。

繼續閱讀