Long Polling vs WebSocket en Java : le duel des technologies temps réel
\n\nImaginez : vous développez un chat, un terminal boursier ou un jeu en ligne en Java. L'utilisateur attend des mises à jour, mais le serveur reste silencieux. Comment faire pour que les données arrivent instantanément ? Avant, il n'y avait qu'une seule solution — Long Polling. Aujourd'hui, WebSocket entre en scène. Mais qui sortira vainqueur de ce combat réel ?
\n\nDans cet article, nous ne nous contenterons pas de comparer les concepts. Nous écrirons du code Java fonctionnel, explorerons les coulisses des deux technologies et déciderons quoi utiliser dans votre prochain projet. Bienvenue sur le ring !
\n\nQu'est-ce que le Long Polling (et pourquoi ce n'est pas un « sondage ») ?
\n\nLe HTTP classique, c'est comme une lettre : on envoie une requête → on reçoit une réponse. Si le serveur n'a rien à dire, il renvoie une réponse vide. Le client est alors obligé de redemander toutes les N secondes. C'est le Short Polling — une impasse.
\n\nLe Long Polling fonctionne plus intelligemment. Le client envoie une requête, et le serveur… s'endort. Il maintient la connexion ouverte jusqu'à ce qu'un nouvel événement se produise (ou que le délai d'attente expire). Dès que l'événement survient, le serveur se réveille et envoie la réponse. Le client crée immédiatement une nouvelle requête Long Polling. La boucle est bouclée.
\n\nLong Polling en Java : une implémentation simple
\n\nImaginons que nous écrivions un serveur de notifications avec l'API Servlet pure (sans Spring, pour plus de clarté). Pour le Long Polling, nous aurons besoin d'un traitement asynchrone (AsyncContext), sinon le serveur bloquera tous les 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 // File d'attente pour stocker les connexions "endormies"\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); // Délai d'attente de 30 secondes\n\n // Sauvegarde du contexte\n WAITING_CLIENTS.add(asyncContext);\n\n // Si le délai expire, on renvoie une réponse vide\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 // Les autres méthodes de l'écouteur sont omises par souci de concision\n });\n }\n\n // Cette méthode est appelée lorsqu'un nouvel événement se produit\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\nComment cela fonctionne :
\n- \n
- Le client effectue une requête GET vers
/long-poll. \n - Le serveur passe la requête en mode asynchrone et « s'endort » (attend). \n
- Lorsqu'un message apparaît (méthode
sendEventToClien