Long Polling 与 WebSocket 在 Java 中的对决:实时技术的较量
\n\n想象一下:您正在用 Java 开发一个聊天应用、股票交易终端或在线游戏。用户等待更新,而服务器却沉默不语。如何让数据瞬间到达?过去只有一种方法——Long Polling。如今,WebSocket 登上了舞台。但在这场实战对决中,谁会胜出?
\n\n本文不仅会对比概念。我们将编写真实的 Java 代码,深入探究这两种技术的内部机制,并决定在您的下一个项目中该使用哪一种。欢迎来到擂台!
\n\n什么是 Long Polling(以及为什么它不是“轮询”)?
\n\n传统的 HTTP 就像一封信:发送请求 → 收到响应。如果服务器无话可说,它会返回一个空响应。客户端不得不每隔 N 秒再次询问。这就是 Short Polling——一条死胡同。
\n\nLong Polling 则更加巧妙。客户端发送请求,而服务器……进入休眠。它保持连接打开,直到有新事件发生(或超时)。一旦事件发生,服务器就会唤醒并发送响应。客户端立即创建一个新的 Long Polling 请求。循环就此闭合。
\n\nJava 中的 Long Polling:简单实现
\n\n假设我们使用纯 Servlet API(不使用 Spring,以便更清晰)编写一个通知服务器。对于 Long Polling,我们需要异步处理(AsyncContext),否则服务器会阻塞所有线程。
\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 // 用于存储“休眠”连接的队列\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); // 超时时间 30 秒\n\n // 保存上下文\n WAITING_CLIENTS.add(asyncContext);\n\n // 如果超时,返回空响应\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 // 其他监听器方法已省略以保持简洁\n });\n }\n\n // 当新事件出现时调用此方法\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\n工作原理:
\n- \n
- 客户端向
/long-poll发起 GET 请求。 \n - 服务器将请求转为异步模式并“休眠”(等待)。 \n
- 当有消息出现时(通过
sendEventToClien