Resolvendo erro de suporte assíncrono em fluxos SSE com Spring Boot após o deploy

Ao implementar fluxos de Server-Sent Events (SSE) no Spring Boot, é comum encontrar cenários onde tudo funciona perfeitamente no ambiante de desenvolvimento local, mas falha imediatamente após o empacotamento (JAR ou TAR) e deploy em ambientes de produção. O erro mais recorrente nestes casos é uma exceção de argumento ilegal relacionada ao processamento assíncrono.

A ntaureza do erro

O problema geralmente se manifesta com a seguinte mensagem no log da aplicação:

java.lang.IllegalArgumentException: Async support must be enabled on a servlet and for all filters involved in async request processing.

Essa falha ocorre porque o container de servlets (geralmente o Tomcat embarcado) exige que tanto o Servlet quanto todos os filtros na cadeia de execução declarem explicitamente que suportam processamento assíncrono. Em ambientes locais, certas configurações automáticas podem mascarar essa necessidade, que se torna crítica ao rodar o artefato final.

Abordagens comuns que podem falhar

Muitas vezes, tenta-se resolver o problema apenas adicionando a anotação @WebFilter(asyncSupported = true) na classe principal ou em filtros específicos. No entanto, devido à ordem de carregamento dos componentes no Spring Boot, essa anotação pode não ser aplicada corretamente a toda a cadeia de filtros gerenciada pelo Spring Security ou outros módulos interceptadores.

Solução definitiva: Configuração Programática de Filtro

A maneira mais robusta de garantir que o suporte assíncrono esteja ativo e seja priorizado é através da criação de um FilterRegistrationBean. Esta abordagem permite definir manualmente a prioridade do filtro e forçar a flag de suporte assíncrono no motor do Tomcat.

Abaixo, apresentamos a implementação de uma classe de configuração para resolver esse gargalo:

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Configuration
public class SseAsyncConfiguration {

    @Bean
    public FilterRegistrationBean<SseOptimizationFilter> sseFilterRegistration() {
        FilterRegistrationBean<SseOptimizationFilter> registration = new FilterRegistrationBean<>();
        
        // Define a implementação do filtro
        registration.setFilter(new SseOptimizationFilter());
        
        // Aplica a todos os endpoints
        registration.addUrlPatterns("/*");
        
        // Ativa explicitamente o suporte assíncrono
        registration.setAsyncSupported(true);
        
        // Define a prioridade máxima para garantir que execute antes de outros filtros
        registration.setOrder(Ordered.HIGHEST_PRECEDENCE);
        
        return registration;
    }

    /**
     * Filtro interno para injetar atributos de suporte assíncrono diretamente no request.
     */
    private static class SseOptimizationFilter extends OncePerRequestFilter {
        @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
                throws ServletException, IOException {
            
            // Força o atributo de suporte assíncrono específico do Tomcat
            request.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", true);
            
            chain.doFilter(request, response);
        }
    }
}

Considerações Adicionais

Além da configuração do filtro, certifique-se de que a sua classe principal de inicialização (Application) contenha a anotação @EnableAsync, permitindo que o Spring gerencie corretamente as threads destinadas a tarefas assíncronas.

Caso esteja utilizando um servidor externo (como um Tomcat standalone) em vez do JAR/TAR executável com servidor embarcado, a configuração pode exigir ajustes adicionais no arquivo web.xml do servidor ou nas definições do conector no server.xml, habilitando o suporte a NIO (Non-blocking I/O).

Tags: Spring Boot Server-Sent Events java Servlet API tomcat

Publicado em 10-7 14:04