Falha no Processamento Assíncrono do Tomcat 7 – apenas uma solicitação processada simultaneamente

Eu estava tentando implementar o bate-papo COMET usando o processamento asynchronous definido na API de Servlet 3. Ele não estava funcionando – o bate-papo foi bloqueado, então eu criei o servlet de debugging para testar somente a parte assíncrona.

Este é o meu método doGet:

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

Estou colocando itens de solicitação na fila, e há um encadeamento em execução, que pegará os itens após o tempo limite especificado e enviará uma resposta ao AsyncContext, imprimindo mensagens sobre isso. O problema é que o encadeamento é bloqueado até que o AsyncContext seja respondido. Isto é o que é visível no meu log depois de solicitar 4 carregamentos de página no navegador:

 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] 

Como é ver, o próximo método do doGet é chamado somente após o pedido anterior ser (teoricamente asynchronous) respondido. Então a coisa toda assíncrona não está funcionando! E aqui está a declaração web.xml:

   TestAsyncServlet my.servlet.TestAsyncServlet true   TestAsyncServlet /test-async  

Estou fazendo tudo para ser encontrado na Internet. Eu não vejo o lugar do erro a ser cometido. Eu encontrei nada de especial para configurar em servlet.xml. Então a questão é: por que não está funcionando como deveria?

OK, como a parte da pesquisa que eu escrevi programa de testes que abriu várias conexões para o tomcat e fez GET / POST no servlet asynchronous. Depurei e verifiquei novamente a configuração do server.xml, o pool de threads limitado para obter uma melhor visibilidade dos resultados do teste, etc. Agora, minha configuração do conector é assim:

  

E isso está funcionando! Eu fiz o teste usando o NIO e fiz 1000 conexões ao mesmo tempo, e todas elas foram processadas de uma só vez.

No entanto, o efeito que descrevi ainda existe no navegador. Quando eu tento carregar o servlet em 10 guias, primeiro é carregado, que segundo etc. Parece ser o comportamento do navegador, nada é bloqueado no servidor. Quando eu abri 3 navegadores (Firefox, Chrome, Opera) eu tenho 3 conexões processadas ao mesmo tempo.

Assim, o processamento asynchronous definido no Servlet API 3.0 funciona no Tomcat 7, no entanto, ele deve ser testado com o próprio programa e não com várias guias no navegador … testar o bate-papo COMET em várias guias também pode não funcionar conforme o esperado. No entanto, no exemplo real, um computador irá abrir apenas uma conexão. E o comportamento do navegador não é culpa de nenhum servidor.

editar Depois que a solução Spring MVC foi incluída no aplicativo da Web, o modo de processamento asynchronous foi interrompido (o parâmetro web.xml foi ignorado). No entanto, o suporte asynchronous pode ser configurado manualmente por meio da adição de linha:

 request.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", true); 

Eu acho que você tem o seu servidor configurado corretamente. Se você está apenas carregando com um navegador, o problema pode ser o modo como o seu navegador está funcionando. Se você acabou de acessar a página da web com o navegador, o navegador tentará carregar a página inteira e bloqueá-la até que ela termine. Seu navegador não terminará a carga até que a solicitação assíncrona seja concluída.

Por exemplo, se você consultasse a mensagem com uma ferramenta como o Fiddler, esperaria ver o seguinte (supondo que o atraso seja 4):

 Browser -> Server [Time: 0] Request Server -> Browser [Time: 0.1] Async Response Server -> Browser [Timer: 4] Complete Response Browser shows page loaded. 

Se você tirou o modo asynchronous que você veria:

 Browser -> Server [Time: 0] Request Server -> Browser [Time: 4] Response Browser shows page loaded. 

Nos dois exemplos, a página não será totalmente carregada até que toda a solicitação seja concluída, embora uma seja assíncrona e uma seja síncrona. Para aproveitar plenamente as solicitações assíncronas, você precisará de um cliente mais inteligente, por exemplo, Javascript ou Flex ou algo do tipo.

Em outras palavras, eu não acho que você pode dizer que o servidor está correto ou não apenas carregando o navegador. Eu pegaria uma ferramenta como o Fiddler e veria exatamente quais mensagens HTTP estão passando.