I was trying to implement COMET chat using Async Processing defined in Servlet API 3. It was not working - chat got blocked, so I have created debug servlet to test async part only.
This is my doGet method:
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
log.debug("doGet called");
int timeout = 30 + RandomUtils.nextInt(60);
String message = RandomStringUtils.randomAlphanumeric(50 + RandomUtils.nextInt(250));
response.setHeader("Access-Control-Allow-Origin", "*");
final AsyncContext context = request.startAsync();
synchronized(items) {
items.add(new RequestItem(context, message, timeout));
}
log.debug("doGet created request and finished");
}
I'm putting request items in queue, and there's a thread running, that will take the items after specified timeout and send response to AsyncContext, printing message about it. The problem is, the thread is blocked until the AsyncContext gets responded. This is what is visible in my log after requesting 4 page loads in browser:
2011-12-08 13:56:36,923 DEBUG [my.servlet.TestAsyncServlet] doGet called
2011-12-08 13:56:36,952 DEBUG [my.servlet.TestAsyncServlet] doGet created request and finished
2011-12-08 13:57:39,934 TRACE [my.servlet.TestAsyncServlet] respond on item RequestItem [context=org.apache.catalina.core.AsyncContextImpl@175870a, message=zEQpATavzwFl6qIbBKve4OzIY9UUuZBwbqN1TC5KpU3i8LM9B6ChgUqaRmcT2yF, timeout=0]
2011-12-08 13:57:39,962 DEBUG [my.servlet.TestAsyncServlet] doGet called
2011-12-08 13:57:39,962 DEBUG [my.servlet.TestAsyncServlet] doGet created request and finished
2011-12-08 13:58:53,949 TRACE [my.servlet.TestAsyncServlet] respond on item RequestItem [context=org.apache.catalina.core.AsyncContextImpl@88ee03, message=pKHKC632CPIk7hGLV0YqCbQl1qpWIoyNv5OWCp21bEqoni1gbY79HT61QEUS2eCjeTMoNEwdqKzCZNGgDngULysSzVdzFTnQQ5cQ8JvcYnp1pLVqGTueJPWnbRdUuO, timeout=0]
2011-12-08 13:58:53,960 DEBUG [my.servlet.TestAsyncServlet] doGet called
2011-12-08 13:58:53,960 DEBUG [my.servlet.TestAsyncServlet] doGet created request and finished
2011-12-08 13:59:36,954 TRACE [my.servlet.TestAsyncServlet] respond on item RequestItem [context=org.apache.catalina.core.AsyncContextImpl@197950e, message=43FPeEUZWBLqgkAqS3WOFMiHUMVvx6o4jNqWLx8kUvwxqJqpOZyGCtiIcr7yw, timeout=0]
2011-12-08 13:59:36,999 DEBUG [my.servlet.TestAsyncServlet] doGet called
2011-12-08 13:59:36,999 DEBUG [my.servlet.TestAsyncServlet] doGet created request and finished
2011-12-08 14:00:34,957 TRACE [my.servlet.TestAsyncServlet] respond on item RequestItem [context=org.apache.catalina.core.AsyncContextImpl@1cb1278, message=r69Y4NQsyR1vj0kzUlHssic2x1Yrr6T09IGKjWAH1E6Lz4VhFTy9dQHi5CPeTObyjLLBDlCLEDfiyMUnVkVIEgYG7r47Ak4w30RklhzdEi9nthqdfNkry6nyjircsFPX534NqWjI1LwsrGq5nOa3ZYtfjfPVpGlk4KDmWP11L53YntO3GmptZPKa50gcqj9i, timeout=0]
As it is to see, the next doGet method is called only after previous request is (theoretically asynchronous) answered. So the whole async thing is not working at all! And here's the web.xml declaration:
<servlet>
<servlet-name>TestAsyncServlet</servlet-name>
<servlet-class>my.servlet.TestAsyncServlet</servlet-class>
<async-supported>true</async-supported>
</servlet>
<servlet-mapping>
<servlet-name>TestAsyncServlet</servlet-name>
<url-pattern>/test-async</url-pattern>
</servlet-mapping>
I'm doing all as to be found in Internet. I don't see the place of mistake to be made. I've found nothing special to configurure in servlet.xml. So the question is, why it is not working as it should?
OK, as the part of the research I've written testing programm that opened multiply connections to tomcat and did GET/POST on the async servlet. I've debugged and rechecked my server.xml configuration, limited thread pool for better visibility of test results etc. Now my configuration of connector looks like that:
<Connector port="8080" protocol="org.apache.coyote.http11.Http11NioProtocol"
minProcessors="3"
maxProcessors="8"
maxThreads="20"
connectionTimeout="150000"
asyncTimeout="150000" />
And this is working! I've made test using NIO and made 1000 connections at one time, and all of them got processed in one time.
However, the effect I've described, still exists in browser. When I try to load servlet on 10 tabs, first get loaded, than second etc. It seems to be browser's behaviour, nothing gets blocked on server. When I opened 3 browsers (Firefox, Chrome, Opera) I've got 3 connections processed on one time.
So, the asynchronous processing defined in Servlet API 3.0 works on Tomcat 7, however, it must be tested with own programm not with multiple tabs in browser... testing COMET chat in multiple tabs can also not work as expected. However, in real-life example one computer will open only one connection. And, the browser's behaviour is not any server's fault.
edit After Spring MVC solution was included into web application, async processing mode was stopped (parameter from web.xml was ignored). However, the async support can be set up manually via adding line:
request.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", true);