我想知道Tomcat 7如何实现异步处理。我了解到请求线程会立即返回,从而允许请求线程立即侦听新请求并对其进行响应。
如何处理“异步”请求?是否有一个单独的线程池来处理异步请求?我假设使用性能类似java.nio.Selector的东西来处理阻塞IO。那么阻塞CPU计算的线程呢?
您正在混淆不同的概念。您必须区分:
ExecutorService
Runnable
AsyncContext
AsyncContext.start()
server.xml
下面的示例概述了它 如何 工作。它仅使用一个线程进行工作者作业。如果您从2个不同的浏览器并行运行它,则输出如下所示(我使用自定义记录器):
DATE THREAD_ID LEVEL MESSAGE 2011-09-03 11:51:22.198 +0200 26 I: >doGet: chrome 2011-09-03 11:51:22.204 +0200 26 I: <doGet: chrome 2011-09-03 11:51:22.204 +0200 28 I: >run: chrome 2011-09-03 11:51:27.908 +0200 29 I: >doGet: firefox 2011-09-03 11:51:27.908 +0200 29 I: <doGet: firefox 2011-09-03 11:51:32.227 +0200 28 I: <run: chrome 2011-09-03 11:51:32.228 +0200 28 I: >run: firefox 2011-09-03 11:51:42.244 +0200 28 I: <run: firefox
您会看到doGet方法立即完成,而工作程序仍在运行。2个测试请求:http://localhost:8080/pc/TestServlet?name=chrome和http://localhost:8080/pc/TestServlet?name=firefox。
doGet
http://localhost:8080/pc/TestServlet?name=chrome
http://localhost:8080/pc/TestServlet?name=firefox
简单示例Servlet
@WebServlet(asyncSupported = true, value = "/TestServlet", loadOnStartup = 1) public class TestServlet extends HttpServlet { private static final Logger LOG = Logger.getLogger(TestServlet.class.getName()); private static final long serialVersionUID = 1L; private static final int NUM_WORKER_THREADS = 1; private ExecutorService executor = null; @Override public void init() throws ServletException { this.executor = Executors.newFixedThreadPool(NUM_WORKER_THREADS); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { final String name = request.getParameter("name"); LOG.info(">doGet: " + name); AsyncContext ac = request.startAsync(); // obtain async context ac.setTimeout(0); // test only, no timeout /* Create a worker */ Runnable worker = new TestWorker(name, ac); /* use your own executor service to execute a worker thread (TestWorker) */ this.executorService.execute(worker); /* OR delegate to the container */ // ac.start(worker); LOG.info("<doGet: " + name); } }
…以及TestWorker
public class TestWorker implements Runnable { private static final Logger LOG = Logger.getLogger(TestWorker.class.getName()); private final String name; private final AsyncContext context; private final Date queued; public TestWorker(String name, AsyncContext context) { this.name = name; this.context = context; this.queued = new Date(System.currentTimeMillis()); } @Override public void run() { LOG.info(">run: " + name); /* do some work for 10 sec */ for (int i = 0; i < 100; i++) { try { Thread.sleep(100); } catch (InterruptedException e) { throw new RuntimeException(e); } } ServletResponse response = this.context.getResponse(); response.setContentType("text/plain"); try { PrintWriter out = response.getWriter(); out.println("Name:\t\t" + this.name); out.println("Queued:\t\t" + this.queued); out.println("End:\t\t" + new Date(System.currentTimeMillis())); out.println("Thread:\t\t" + Thread.currentThread().getId()); out.flush(); } catch (IOException e) { throw new RuntimeException(e); } this.context.complete(); LOG.info("<run: " + name); } }