來自掘金:超人汪小建
??有時(shí)Servlet在生成響應(yīng)報(bào)文前必須等待某些耗時(shí)的操作誊锭,比如在等待一個(gè)可用的JDBC連接或等待一個(gè)遠(yuǎn)程Web服務(wù)的響應(yīng)恬口。對(duì)于這種情況servlet規(guī)范中定義了異步處理方式校读,由于Servlet中等待阻塞會(huì)導(dǎo)致Web容器整體的處理能力低下,所以對(duì)于比較耗時(shí)的操作可以放置到另外一個(gè)線程中進(jìn)行處理祖能,此過程保留連接的請(qǐng)求和響應(yīng)對(duì)象歉秫,在處理完成之后可以把處理的結(jié)果通知到客戶端。
??下面先看Servlet在同步情況下的處理過程养铸,如圖所示雁芙,Tomcat的客戶端請(qǐng)求由管道處理最后會(huì)通過Wrapper容器的管道,這時(shí)它會(huì)調(diào)Servlet實(shí)例的service方法進(jìn)行邏輯處理钞螟,處理完后響應(yīng)客戶端兔甘,整個(gè)處理由Tomcat的Executor線程池的線程處理,而線程池的最大線程數(shù)使有限制的鳞滨,所以這個(gè)處理過程越短裂明、越快把線程讓回線程池就越好。但如果Servlet中的處理邏輯耗時(shí)越長就會(huì)導(dǎo)致長期地占用Tomcat的處理線程池太援,影響Tomcat的整體處理能力闽晦。
??為了解決上面的問題引入了支持異步的Servlet,同樣是客戶端請(qǐng)求到來提岔,然后通過管道最后進(jìn)入到Wrapper容器的管道仙蛉,調(diào)用Servlet實(shí)例的service后,創(chuàng)建一個(gè)異步上下文將耗時(shí)的邏輯操作封裝起來碱蒙,交給用戶自己定義的線程池荠瘪,這時(shí)Tomcat的處理線程就能馬上回到Executor線程池夯巷,而不用等待耗時(shí)的操作完成才讓出線程,從而提升了Tomcat的整體處理能力哀墓。這里要注意的是趁餐,由于后面做完耗時(shí)的操作后還需要對(duì)客戶端響應(yīng),所以需要保持住Request和Response對(duì)象篮绰,以便輸出響應(yīng)報(bào)文到客戶端后雷。
再結(jié)合一個(gè)簡(jiǎn)單的異步代碼來看Tomcat對(duì)Servlet異步的實(shí)現(xiàn):
public class AsyncServlet extends HttpServlet {
ScheduledThreadPoolExecutor userExecutor = new ScheduledThreadPoolExecutor(5);
public void doGet(HttpServletRequest req, HttpServletResponse res) {
AsyncContext aCtx = req.startAsync(req, res);
userExecutor.execute(new AsyncHandler(aCtx));
}
}
public class AsyncHandler implements Runnable {
private AsyncContext ctx;
public AsyncHandler(AsyncContext ctx) {
this.ctx = ctx;
}
@Override
public void run() {
//耗時(shí)操作
PrintWriter pw;
try {
pw = ctx.getResponse().getWriter();
pw.print("done!");
pw.flush();
pw.close();
} catch (IOException e) {
e.printStackTrace();
}
ctx.complete();
}
}
??我們創(chuàng)建一個(gè)AsyncServlet,它定義了一個(gè)userExecutor線程池專門用于處理該Servlet的所有請(qǐng)求的耗時(shí)的邏輯操作吠各。這樣就不會(huì)占用Tomcat內(nèi)部的Executor線程池臀突,影響到對(duì)其他Servlet的處理。這種思想有點(diǎn)像資源隔離贾漏,耗時(shí)的操作統(tǒng)一由指定的線程池處理候学,而不要影響其它耗時(shí)少的請(qǐng)求處理。
Servlet的異步的實(shí)現(xiàn)就很好理解了纵散,startAsync方法其實(shí)就是創(chuàng)建了一個(gè)異步上下文AsyncContext對(duì)象梳码,該對(duì)象封裝了請(qǐng)求和響應(yīng)對(duì)象。然后創(chuàng)建一個(gè)任務(wù)用于處理耗時(shí)邏輯伍掀,后面通過AsyncContext對(duì)象獲得響應(yīng)對(duì)象并對(duì)客戶端響應(yīng)边翁,輸出“done!”。完成后要通過complete方法告訴Tomcat內(nèi)部我已經(jīng)處理完硕盹,Tomcat就會(huì)請(qǐng)求對(duì)象和響應(yīng)對(duì)象進(jìn)行回收處理或關(guān)閉連接符匾。