Servlet 3的异步Servlet功能 - 如何处理耗时的任务

时间:2014-06-04 06:20:08

标签: java multithreading tomcat servlets asynchronous

我现在正在学习Servlet 3.0异步功能,它的主要思想是将绑定到Http线程的任务发布到另一个线程,这样Http线程就可以回到Http线程池了(没有阻止长任务处理),那么你的应用程序可能会更具响应性。一切都很好。


我找到了两种处理耗时任务的方法

  1. acontext.start()

      asyncContext.start(new Runnable() {
            @Override
            public void run() {
                serviceImpl(req, resp, adapter, context, isNotLeakScan);
            }
        });
    

    官方文件说: acontext.start(new Runnable() {...}) gets a new thread from the container.

  2. 使用BlockingQueue,然后使用新的Tread处理队列中的runnables。

    private static final BlockingQueue queue = new LinkedBlockingQueue();
    
    
    thread = new Thread(new Runnable() {
    @Override
    public void run() {
    while (true) {
      try {
        Thread.sleep(2000);
        AsyncContext context;
        while ((context = queue.poll()) != null) {
          try {
            ServletResponse response = context.getResponse();
            response.setContentType("text/plain");
            PrintWriter out = response.getWriter();
            out.printf("Thread %s completed the task",  
            Thread.currentThread().getName());
            out.flush();
          } catch (Exception e) {
            throw new RuntimeException(e.getMessage(), e);
          } finally {
            context.complete();
          }
        }
      } catch (InterruptedException e) {
        return;
      }
    }
    

    }


  3. 我的问题是:

    • 这两种方法有什么区别?

    • 第一个是否将任务管理处理到Tomcat容器(假设我们已经在Tomcat上部署了应用程序)

    • 第二种方式只是手动显示处理任务的方式?

1 个答案:

答案 0 :(得分:2)

区别在于:

(1)在servlet线程池中执行你的慢任务,导致servlet池饥饿(至少是Tomcat / Jetty的情况)。这意味着如果用serviceImpl(req, resp, adapter, context, isNotLeakScan);替换Thread.sleep(Long.MAX_VALUE);并尝试从浏览器连接到Tomcat 200次(200是默认线程数) - Tomcat将永远挂起,不接受任何更多HTTP连接。

(2)手动为每个任务生成一个线程。

您通常不想手动生成线程,轮询队列等。相反,您将使用Executors框架,它具有引擎下的队列实现,并且基于内部线程池。

@WebServlet(urlPatterns = {"/slowServlet"}, asyncSupported = true)
public class SlowServlet extends javax.servlet.http.HttpServlet {

    protected void doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {
        final AsyncContext acontext = request.startAsync();
        ServletContext appScope = request.getServletContext();
        ((Executor) appScope.getAttribute("executor")).execute(() -> {
            try {
                Thread.sleep(10000); // your slow running task
                acontext.getResponse().getWriter().print("Done");
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    }
}

servlet上下文监听器是(线程池的大小在这里是硬编码的)

@WebListener
public class ExecutorListener implements ServletContextListener {

    public void contextInitialized(ServletContextEvent sce) {
        Executor executor = Executors.newFixedThreadPool(10);
        sce.getServletContext().setAttribute("executor", executor);
    }

    public void contextDestroyed(ServletContextEvent sce) {
        // add executor fancy shutdown logic here
    }
}