异步Servlet

本文介绍了异步Servlet的概念及其优势,并对比了同步Servlet的处理方式。文章详细解释了异步Servlet如何通过分离耗时操作来提高Web容器的处理能力,包括初始化线程池、监听异步任务生命周期以及具体的业务逻辑实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

异步Servlet

有的时候servlet在相应报文之前会有一些耗时操作,比如JDBC的操作,或者等待另一个远程Web的响应,同步Servlet中等待阻塞会导致Web容器整体的处理能力低下。对于这种情况可使用servlet异步处理方式,把比较耗时的操作可以放置到另外一个线程中进行处理,此过程保留连接的请求和响应对象,在处理完成之后可以把处理的结果通知到客户端。

同步Servlet

如图所示,Tomcat的客户端请求由管道处理最后会通过Wrapper容器的管道,这时它会调Servlet实例的service方法进行逻辑处理,处理完后响应客户端,整个处理由Tomcat的Executor线程池的线程处理。

img

缺点:遇见处理逻辑耗时较长的任务会长时间的占用tomcat的处理线程,影响tomcat的整体处理能力。特别是当你的线程数有限比如只有5个,同时遇见5个耗时100ms的请求,那么在这100ms内的其它请求都会失败,严重影响的服务的吞吐量和稳定性。

优点:实现逻辑简单易懂,对于大量的耗时短的任务性能好。

异步Servlet

异步Servlet是在3.0版本引入的,客户端请求到来,然后通过管道最后进入到Wrapper容器的管道,调用Servlet实例的service后,创建一个异步上下文将耗时的逻辑操作封装起来,交给用户自己定义的线程池,这时Tomcat的处理线程就能马上回到Executor线程池,而不用等待耗时的操作完成才让出线程,从而提升了Tomcat的整体处理能力。

img

一个��

初始化线程池


@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();  
    }  

}

编写一个监听器,监听异步任务生命周期中的一些动作。

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的实现类中

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 注解自动注册,无需其他代码。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值