Hi there, I'm studying Servlet 3's async API using tomcat. I see following strange behavior from tomcat in a very simple test app!
I have following JMeter test plan: Number of threads (users): 700 Ramp-Up period (in seconds): 23 Loop count: 1 So JMeter generates 30.43 requests per second and 304.3 requests per 10 seconds. I'm planning to full tomcat's BIO pool and accept buffer :) I have an always async test servlet which on destroy, I print tomcat container max used threads count. It prints 187 for me which is lower than 200 (tomcat default pool size) so I should not get any "connection refuse" but I get! I have simulated a blocking operation by a sleep for 10 seconds. When my servlet gets a request, it quickly starts an async and add further processing to my own thread pool (container thread comes back to pool quickly, right). My own thread pool size is 310 which is greater than 304.3 (requests in 10 seconds) so never full. I've tested several times. Tomcat successfully returns from all requests below 326th but fails 102 requests from 326th to 700th with "connection refuse" and afew with "connection reset". Why?! My own thread pool does the jobs and Tomcat's pool is free (my servlet uses 187 threads of tomcat at max). Thanks in advance! JMETER RESULT of RESPONSE TIMES: Max: 60 seconds (lower then tomcat and asyncContext timeout) MIN: 10 seconds AVG: 37 seconds ERR: 15% CONFIGURATIONS: Server.xml <Connector port="7780" protocol="org.apache.coyote.http11.Http11Protocol" connectionTimeout="120000" redirectPort="7743" /> Async.java package com.sstr.example; import javax.servlet.*; import javax.servlet.annotation.WebInitParam; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; @WebServlet( name = "async", value = {"/async"}, asyncSupported = true, initParams = { @WebInitParam(name = "JobPoolSize", value = "310") } ) public class Async extends HttpServlet { public final int REQUEST_TIMEOUT = 120000; private ExecutorService exe; @Override public void init() throws ServletException { int size = Integer.parseInt(getInitParameter("JobPoolSize")); exe = Executors.newFixedThreadPool( size, new ThreadFactory() { @Override public Thread newThread(Runnable r) { return new Thread(r, "Async Processor"); } } ); } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { final AsyncContext context = req.startAsync(); context.setTimeout(REQUEST_TIMEOUT); exe.execute(new ContextExecution(context, Thread.currentThread().getName())); } @Override public void destroy() { System.out.println("Container MAX used threads: " + threadCount); exe.shutdown(); } int threadCount = 0; class ContextExecution implements Runnable { final AsyncContext context; final String containerThreadName; public ContextExecution(AsyncContext context, String containerThreadName) { this.context = context; this.containerThreadName = containerThreadName; } @Override public void run() { try { int threadNumber = Integer.parseInt(containerThreadName.substring( containerThreadName.lastIndexOf('-')+1)); if(threadNumber > threadCount) { threadCount = threadNumber; } // Simulate Time Consuming Task Thread.sleep(10000); ServletResponse resp = context.getResponse(); if (resp != null) { resp.getWriter().write("Ok"); } context.complete(); } catch (Exception e) { // Handle ? } } } } OUTPUT: Container MAX used threads: 187