異步servlet總結java
服務端須要tomcat8.0及以上web
jdk須要8及以上tomcat
須要顯式開啓異步servlet配置,方法有兩種:併發
<servlet>異步
<servlet-name>DemoServlet</servlet-name>async
<servlet-class>footmark.servlet.Demo Servlet</servlet-class>ide
<async-supported>true</async-supported>函數
</servlet>this
@WebFilter(urlPatterns = "/demo",asyncSupported = true)編碼
static private ScheduledThreadPoolExecutor userExecutor =
new ScheduledThreadPoolExecutor(5);
C、 在run方法中,從異步上下文環境中獲得request和response,編碼實現耗時的應用邏輯,以後經過respones將下行報文返回給客戶端
public class AsyncHandler implements Runnable{
private AsyncContext ctx;
public AsyncHandler(AsyncContext ctx) {
this.ctx = ctx;
}
public void run() {
PrintWriter pw;
try {
pw = ctx.getResponse().getWriter();
pw.print("done");
pw.flush();
pw.close();
} catch(IOException e) {
e.printStackTrace();
}
ctx.complete();
}
}
AsyncContext aCtx = request.startAsync(request,response);
userExecutor.execute(new AsyncHandler(aCtx));
一個應用邏輯在tomcat中所需的處理時間分兩塊:等待進入tomcat內部線程池上運行的時間和應用的真實處理時間(實際上不止這兩塊時間,但此處爲了方便分析作了簡化)。
tomcat內部線程池的線程數量是有限的,若是應用的執行時間很長,就會長時間佔用線程,使得線程不能很快地回到線程池。這樣新的請求可能由於線程池中沒有就緒的線程而不得不長時間的等待。這一方面會使得tomcat的併發數不能獲得提高,另外一方面使得用戶體驗不夠好。
爲了解決這個問題,能夠在servlet內部新建一個內部線程池,當新的請求到來以後,會在tomcat內部的線程池中取出一個線程運行,但這個tomcat的線程並不真正處理那些耗時的業務邏輯,而是啓動一個異步上下文,並將那些耗時的業務邏輯委託給servlet內部線程池去運行。tomcat的線程委託完以後本身就能夠很快的返回tomcat的線程池中,以待新的請求到來。
servlet內部的線程池處理完耗時的業務請求以後,須要返回下行報文給客戶端,因此它須要reqeust對象和resoponse對象,這就是爲何須要給異步handler傳入異步上下文的緣由---------在異步上下文中,能夠獲得request和response對象。
異步servlet技術並不能縮短應用邏輯處理的時間,這個時間不管如何是無法縮短的。然而,經過將應用邏輯的執行委託給servlet內部的線程池,tomcat的線程池就不須要本身親自去執行這些耗時的邏輯了,所以tomcat的執行線程能夠很快地返回本身的線程池中,這帶來兩個好處:tomcat能夠處理更多的請求,換而言之就是提升了系統的併發量;新的請求可能無需等待就能及時被tomcat內部的線程池委託到servlet的內部線程池上,這個可能的等待時間被消滅了,換而言之就是從總體上減小了系統的響應時間。
但要說明的是,應用那些耗時的邏輯,不管是直接在tomcat內部線程池上運行,仍是在servlet的內部現場池上運行,這個時間是沒法縮短的。
源碼
一、AsyncHandler.java
package com.nari;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.AsyncContext;
public class AsyncHandler implements Runnable{
private AsyncContext ctx;
public AsyncHandler(AsyncContext ctx) {
this.ctx = ctx;
}
@Override
public void run() {
PrintWriter pw;
try {
pw = ctx.getResponse().getWriter();
pw.print("done");
pw.flush();
pw.close();
} catch(IOException e) {
e.printStackTrace();
}
ctx.complete();
}
}
2.AsyncServlet.java
package com.nari;
import java.io.IOException;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import javax.servlet.AsyncContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet(urlPatterns = "/AsyncServlet", asyncSupported = true)
public class AsyncServlet extends HttpServlet {
/**
*
*/
private static final long serialVersionUID = 1L;
static private ScheduledThreadPoolExecutor userExecutor = new ScheduledThreadPoolExecutor(5);
public AsyncServlet() {
super();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
AsyncContext aCtx = request.startAsync(request,response);
userExecutor.execute(new AsyncHandler(aCtx));
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}