Long Polling vs WebSocket in Java: A Duel of Real-Time Technologies
\n\nImagine: you are developing a chat, a stock exchange terminal, or an online game in Java. The user waits for updates, but the server is silent. How do you make data arrive instantly? There used to be only one solution — Long Polling. Today, WebSocket is in the arena. But who will win in a real fight?
\n\nIn this article, we will not just compare concepts. We will write live Java code, look at the internals of both technologies, and decide what to use in your next project. Welcome to the ring!
\n\nWhat is Long Polling (and why is it not "polling")?
\n\nClassic HTTP is like a letter: send a request → receive a response. If the server has nothing to say, it returns an empty response. The client is forced to ask again every N seconds. This is Short Polling — a dead-end path.
\n\nLong Polling works smarter. The client sends a request, and the server… goes to sleep. It keeps the connection open until a new event occurs (or a timeout expires). As soon as an event occurs, the server wakes up and sends a response. The client immediately creates a new Long Polling request. The cycle is closed.
\n\nLong Polling in Java: A Simple Implementation
\n\nLet's imagine we are writing a notification server using the pure Servlet API (without Spring, to make it clearer). For Long Polling, we will need asynchronous processing (AsyncContext), otherwise the server will block all 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 // Queue to store "sleeping" connections\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 30 seconds\n\n // Save the context\n WAITING_CLIENTS.add(asyncContext);\n\n // If timeout expires — return an empty response\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 // Other listener methods omitted for brevity\n });\n }\n\n // This method is called when a new event occurs\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\nHow it works:
\n- \n
- The client makes a GET request to
/long-poll. \n - The server switches the request to asynchronous mode and "goes to sleep" (waits). \n
- When a message appears (the
sendEventToClien