異步Servlet

異步Servlet

有的時候servlet在相應報文以前會有一些耗時操做,好比JDBC的操做,或者等待另外一個遠程Web的響應,同步Servlet中等待阻塞會致使Web容器總體的處理能力低下。對於這種狀況可以使用servlet異步處理方式,把比較耗時的操做能夠放置到另一個線程中進行處理,此過程保留鏈接的請求和響應對象,在處理完成以後能夠把處理的結果通知到客戶端。java

同步Servlet

如圖所示,Tomcat的客戶端請求由管道處理最後會經過Wrapper容器的管道,這時它會調Servlet實例的service方法進行邏輯處理,處理完後響應客戶端,整個處理由Tomcat的Executor線程池的線程處理。apache

![img](file:///var/folders/74/8gxv5r0j3xvflks6fxth63540000gn/T/WizNote/de95c2ac-493a-4c0b-ac87-f45447bb6345/index_files/73010586.png)tomcat

缺點:碰見處理邏輯耗時較長的任務會長時間的佔用tomcat的處理線程,影響tomcat的總體處理能力。特別是當你的線程數有限好比只有5個,同時碰見5個耗時100ms的請求,那麼在這100ms內的其它請求都會失敗,嚴重影響的服務的吞吐量和穩定性。app

優勢:實現邏輯簡單易懂,對於大量的耗時短的任務性能好。異步

異步Servlet

異步Servlet是在3.0版本引入的,客戶端請求到來,而後經過管道最後進入到Wrapper容器的管道,調用Servlet實例的service後,建立一個異步上下文將耗時的邏輯操做封裝起來,交給用戶本身定義的線程池,這時Tomcat的處理線程就能立刻回到Executor線程池,而不用等待耗時的操做完成才讓出線程,從而提高了Tomcat的總體處理能力。async

![img](file:///var/folders/74/8gxv5r0j3xvflks6fxth63540000gn/T/WizNote/de95c2ac-493a-4c0b-ac87-f45447bb6345/index_files/73445233.png)ide

一個🌰

初始化線程池性能

@WebListener  
public class AppContextListener implements ServletContextListener {  
   
    public void contextInitialized(ServletContextEvent servletContextEvent) {  
   
        // create the thread pool 
        ThreadPoolExecutor executor = new ThreadPoolExecutor(100, 200, 50000L,  
                TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(100));  
        servletContextEvent.getServletContext().setAttribute("executor",  
                executor);  
   
    }  
   
    public void contextDestroyed(ServletContextEvent servletContextEvent) {  
        ThreadPoolExecutor executor = (ThreadPoolExecutor) servletContextEvent  
                .getServletContext().getAttribute("executor");  
        executor.shutdown();  
    }  
   
}
複製代碼

編寫一個監聽器,監聽異步任務生命週期中的一些動做。this

public class AppAsyncListener implements AsyncListener {  
   
    @Override  
    public void onComplete(AsyncEvent asyncEvent) throws IOException {  
        System.out.println("AppAsyncListener onComplete");  
        // we can do resource cleanup activity here 
    }  
   
    @Override  
    public void onError(AsyncEvent asyncEvent) throws IOException {  
        System.out.println("AppAsyncListener onError");  
        //we can return error response to client 
    }  
   
    @Override  
    public void onStartAsync(AsyncEvent asyncEvent) throws IOException {  
        System.out.println("AppAsyncListener onStartAsync");  
        //we can log the event here 
    }  
   
    @Override  
    public void onTimeout(AsyncEvent asyncEvent) throws IOException {  
        System.out.println("AppAsyncListener onTimeout");  
        //we can send appropriate response to client 
        ServletResponse response = asyncEvent.getAsyncContext().getResponse();  
        PrintWriter out = response.getWriter();  
        out.write("TimeOut Error in Processing");  
    }  
   
} 
複製代碼

具體的業務邏輯放在一個Runable的實現類中url

public class AsyncRequestProcessor implements Runnable {  
   
    private AsyncContext asyncContext;  
    private int secs;  
   
    public AsyncRequestProcessor() {  
    }  
   
    public AsyncRequestProcessor(AsyncContext asyncCtx, int secs) {  
        this.asyncContext = asyncCtx;  
        this.secs = secs;  
    }  
   
    @Override  
    public void run() {  
        System.out.println("Async Supported? "  
                + asyncContext.getRequest().isAsyncSupported());  
        longProcessing(secs);  
        try {  
            PrintWriter out = asyncContext.getResponse().getWriter();  
            out.write("Processing done for " + secs + " milliseconds!!");  
        } catch (IOException e) {  
            e.printStackTrace();  
        }  
        //complete the processing 
        asyncContext.complete();  
    }  
   
    private void longProcessing(int secs) {  
        // wait for given time before finishing 
        try {  
            Thread.sleep(secs);  
        } catch (InterruptedException e) {  
            e.printStackTrace();  
        }  
    }  
}  
複製代碼

最後,Async Servlet 實現

@WebServlet(urlPatterns = "/AsyncLongRunningServlet", asyncSupported = true)
public class AsyncLongRunningServlet extends HttpServlet {

    private static final long serialVersionUID = 1L;

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        long startTime = System.currentTimeMillis();
        System.out.println("AsyncLongRunningServlet Start::Name="
                + Thread.currentThread().getName() + "::ID="
                + Thread.currentThread().getId());

        request.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", true);

        String time = request.getParameter("time");
        int secs = Integer.valueOf(time);
        // max 10 seconds
        if (secs > 10000) {
            secs = 10000;
        }

        AsyncContext asyncCtx = request.startAsync();
        asyncCtx.addListener(new AppAsyncListener());
        asyncCtx.setTimeout(9000);

        ThreadPoolExecutor executor = (ThreadPoolExecutor) request
                .getServletContext().getAttribute("executor");

        executor.execute(new AsyncRequestProcessor(asyncCtx));
        long endTime = System.currentTimeMillis();
        System.out.println("AsyncLongRunningServlet End::Name="
                + Thread.currentThread().getName() + "::ID="
                + Thread.currentThread().getId() + "::Time Taken="
                + (endTime - startTime) + " ms.");
    }
}
複製代碼

PS:在 SpringBootApplication 上使用@ServletComponentScan 註解後,Servlet、Filter、Listener 能夠直接經過 @WebServlet、@WebFilter、@WebListener 註解自動註冊,無需其餘代碼。

相關文章
相關標籤/搜索