Long Polling vs WebSocket em Java: duelo de tecnologias de tempo real
\n\nImagine: você está desenvolvendo um chat, um terminal de ações ou um jogo online em Java. O usuário espera por atualizações, mas o servidor fica em silêncio. Como fazer os dados chegarem instantaneamente? Antes havia apenas uma saída — Long Polling. Hoje, a arena é do WebSocket. Mas quem vence na luta real?
\n\nNeste artigo, não vamos apenas comparar conceitos. Vamos escrever código vivo em Java, observar os detalhes internos de ambas as tecnologias e decidir o que usar no seu próximo projeto. Bem-vindo ao ringue!
\n\nO que é Long Polling (e por que não é "polling")?
\n\nO HTTP clássico é como uma carta: enviou requisição → recebeu resposta. Se o servidor não tem nada a dizer, ele retorna uma resposta vazia. O cliente é forçado a perguntar novamente a cada N segundos. Isso é Short Polling — um caminho sem saída.
\n\nLong Polling funciona de forma mais inteligente. O cliente envia uma requisição, e o servidor… adormece. Ele mantém a conexão aberta até que um novo evento ocorra (ou até o tempo limite expirar). Assim que o evento acontece — o servidor acorda e envia a resposta. O cliente imediatamente cria uma nova requisição Long Polling. O ciclo se fecha.
\n\nLong Polling em Java: implementação simples
\n\nVamos imaginar que estamos escrevendo um servidor de notificações usando apenas Servlet API (sem Spring, para ficar mais claro). Para Long Polling, precisaremos de processamento assíncrono (AsyncContext), caso contrário o servidor bloqueará todas as threads.
\n\nimport javax.servlet.AsyncContext;\nimport javax.servlet.annotation.WebServlet;\nimport javax.servlet.http.HttpServlet;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\nimport java.util.Queue;\nimport java.util.concurrent.ConcurrentLinkedQueue;\n\n@WebServlet(urlPatterns = "/long-poll", asyncSupported = true)\npublic class LongPollingServlet extends HttpServlet {\n\n // Fila para armazenar conexões "adormecidas"\n private static final Queue<AsyncContext> WAITING_CLIENTS = new ConcurrentLinkedQueue<>();\n\n @Override\n protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {\n AsyncContext asyncContext = req.startAsync();\n asyncContext.setTimeout(30_000); // Timeout de 30 segundos\n\n // Salva o contexto\n WAITING_CLIENTS.add(asyncContext);\n\n // Se o timeout expirar — retorna resposta vazia\n asyncContext.addListener(new javax.servlet.AsyncListener() {\n public void onTimeout(javax.servlet.AsyncEvent event) {\n WAITING_CLIENTS.remove(asyncContext);\n try {\n HttpServletResponse response = (HttpServletResponse) event.getAsyncContext().getResponse();\n response.getWriter().write("timeout");\n response.setStatus(HttpServletResponse.SC_OK);\n asyncContext.complete();\n } catch (IOException e) { e.printStackTrace(); }\n }\n // Outros métodos do listener omitidos por brevidade\n });\n }\n\n // Este método é chamado quando um novo evento ocorre\n public static void sendEventToClients(String message) {\n for (AsyncContext ctx : WAITING_CLIENTS) {\n try {\n HttpServletResponse response = (HttpServletResponse) ctx.getResponse();\n response.getWriter().write(message);\n response.setStatus(HttpServletResponse.SC_OK);\n ctx.complete();\n WAITING_CLIENTS.remove(ctx);\n } catch (IOException e) { e.printStackTrace(); }\n }\n }\n}\n\nComo funciona:
\n- \n
- O cliente faz uma requisição GET para
/long-poll. \n - O servidor coloca a requisição em modo assíncrono e "adormece" (espera). \n
- Quando uma mensagem aparece (método
sendEventToClien\n