ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Modern JavaScript Tutorial 长轮询(Long Polling)实战:无需 WebSocket 的实时消息推送方案

Modern JavaScript Tutorial 长轮询(Long Polling)实战:无需 WebSocket 的实时消息推送方案 文档/教程前端【免费下载链接】en.javascript.infoModern JavaScript Tutorial项目地址https://gitcode.com/gh_mirrors/en/en.javascript.info点击查看免费下载长轮询Long Polling是《现代 JavaScript 教程》网络请求章节中介绍的一种最简实时通信方案它不需要 WebSocket 或 Server Sent Events 这类专用协议仅凭普通的 HTTP 请求配合fetch就能在浏览器与服务器之间实现近乎零延迟的消息推送。读完本文你将掌握长轮询的工作原理、客户端subscribe循环的完整写法、服务端挂起连接的管理要点并能直接运行仓库中提供的完整聊天室示例longpoll.view。普通轮询Regular Polling及其痛点要理解长轮询先看它的前身——普通轮询。最朴素的做法是让客户端每隔固定时间例如每 10 秒向服务器发起一次请求询问“我还在线有新消息吗”。服务器收到请求后做两件事一是记录客户端仍在线二是把截至当前时刻积攒的消息打包返回。这种方式能工作但存在两个明显的缺陷消息延迟最高可达轮询间隔如果消息恰好刚发走客户端要等满一个周期比如 10 秒才能收到下一条。服务器被无效请求轰炸即使没有任何新消息客户端也会每 10 秒发起一次请求哪怕用户已经切到别的标签页甚至睡着了请求仍在持续产生这在性能上是一笔不小的负载。因此普通轮询只适合消息量极小的微型服务一般情况下都需要改进——改进方案就是长轮询。长轮询的核心流程长轮询是对普通轮询的优化实现同样简单却能消除消息延迟。它的工作流分为四步浏览器向服务器发送请求服务器收到请求后不立即响应而是挂起连接直到有消息可发一旦消息产生服务器用这条消息作为响应体返回结束本次请求浏览器收到响应后立刻发起下一个新请求。于是浏览器始终持有一条“已发出但未返回”的挂起连接与服务器相连这是长轮询的常态。只有当消息送达时连接才会关闭然后立刻重建如果连接因网络错误等原因意外中断浏览器会立即发送一个新请求而不是等待重试间隔这保证了消息不会因一次断连而丢失。客户端实现subscribe递归函数文档给出了一个可直接投入使用的客户端subscribe函数骨架其核心思路是“请求 → 等待 → 处理 → 递归重发”async function subscribe() { let response await fetch(/subscribe); if (response.status 502) { // Status 502 is a connection timeout error, // may happen when the connection was pending for too long, // and the remote server or a proxy closed it // lets reconnect await subscribe(); } else if (response.status ! 200) { // An error - lets show it showMessage(response.statusText); // Reconnect in one second await new Promise(resolve setTimeout(resolve, 1000)); await subscribe(); } else { // Get and show the message let message await response.text(); showMessage(message); // Call subscribe() again to get the next message await subscribe(); } } subscribe();逐段拆解其容错逻辑502 Bad Gateway 分支这是长轮询最常遇到的状况。当连接挂起过久远程服务器或中间代理如 Nginx会主动掐断连接表现为 502。此时不需要等待直接递归重连其他非 200 状态分支属于真实错误先把response.statusText展示给用户然后等待 1 秒再重连避免错误状态下疯狂打请求200 成功分支读取消息文本、展示然后立刻递归调用自身去获取下一条消息。可以看到subscribe每次调用都会发起一次fetch然后一直等待响应、处理结果、再调用自己形成一个首尾相接的循环。这条消息通道是单向的服务器 → 浏览器。客户端要发消息时走的是另一条独立通道通常是普通的 POST 请求。服务端要点必须能承载大量挂起连接长轮询对服务端架构有一个硬性要求——服务器必须能同时处理大量挂起中的连接某些服务端架构采用“每连接一个进程”模型如部分 PHP、Ruby 后端有多少连接就有多少进程而每个进程都消耗可观内存连接一多内存就被吃光使用 Node.js 编写的服务器通常没有这类问题因为事件驱动、单线程异步模型天然适合挂起大量连接但文档特别强调这并非编程语言本身的问题。PHP、Ruby 等语言同样可以实现支持大量并发连接的健壮后端关键在于确认自己的服务器架构能承受大量同时存在的连接。完整示例本地可运行的聊天室仓库在 5-network/10-long-polling/longpoll.view 下提供了一个可直接下载并在本地运行的聊天室演示需要 Node.js 环境并安装依赖模块包含三个文件index.html页面结构加载browser.js提供发布消息的表单与消息展示区域browser.js客户端逻辑含PublishForm发消息与SubscribePane收消息两个构造函数server.js基于原生http模块与node-static的服务器实现。客户端发布与订阅两条通道browser.js将文档中的subscribe函数封装成了SubscribePane组件并通过fetch(url)轮询获取消息容错逻辑与文档骨架完全一致502 立即重连、其他错误 1 秒后重连、200 展示后继续订阅。与之配套的PublishForm则用普通 POST 发送消息// Sending messages, a simple POST function PublishForm(form, url) { function sendMessage(message) { fetch(url, { method: POST, body: message }); } form.onsubmit function() { let message form.message.value; if (message) { form.message.value ; sendMessage(message); } return false; }; }在index.html中两者被实例化并关联到页面上对应的表单与容器订阅地址还附加了?random随机参数以规避任何可能的缓存问题script new PublishForm(document.forms.publish, publish); // random url parameter to avoid any caching issues new SubscribePane(document.getElementById(subscribe), subscribe?random Math.random()); /script服务端订阅表驱动的挂起连接管理server.js是理解长轮询服务端原理的最佳范本其核心是一个“订阅者表”let subscribers Object.create(null); function onSubscribe(req, res) { let id Math.random(); res.setHeader(Content-Type, text/plain;charsetutf-8); res.setHeader(Cache-Control, no-cache, must-revalidate); subscribers[id] res; req.on(close, function() { delete subscribers[id]; }); }关键点逐一说明用Object.create(null)创建订阅表避免原型链上属性带来的干扰每个订阅请求生成一个随机id把响应对象res存入表——注意此时响应并未结束连接被有意挂起这正是“长轮询”的“长”之所在设置Content-Type: text/plain;charsetutf-8与Cache-Control: no-cache, must-revalidate防止代理或浏览器缓存干扰实时消息监听req的close事件客户端断开时自动从表中清除对应订阅避免内存泄漏。发布消息时遍历订阅表向每个挂起的连接写入消息并结束响应然后清空表function publish(message) { for (let id in subscribers) { let res subscribers[id]; res.end(message); } subscribers Object.create(null); }请求分发由accept函数完成/subscribe路径挂起连接等待消息/publish路径POST在收齐请求体后调用publish(message)广播给所有订阅者并回复ok其余请求交给node-static静态文件服务器处理。server.js还设计了可测试的导出结构直接运行时通过http.createServer(accept).listen(8080)在 8080 端口启动服务被模块引用时导出accept并支持通过process.send的shutdown消息或SIGINT信号调用close()统一结束所有挂起连接。适用场景消息稀疏时的理想选择长轮询在消息产生频率低的场景下表现出色例如聊天室中用户发言不密集、通知推送等。一旦消息来得非常频繁前面那张“请求-响应”示意图就会变成锯齿状每条消息都是一次独立请求都要携带请求头、经过认证开销等消息密度越高这些固定开销越显得浪费高频场景应改用专用方案WebSocket双向、低开销、适合实时交易等连续数据交换或 Server Sent Events单向、基于普通 HTTP、内置自动重连。作为对比长轮询的全部优势恰恰在于它的“轻”不引入新协议、实现成本极低、只需普通 HTTP 基础设施即可工作。选择实时通信方案时可以按“消息频率低 → 长轮询消息频繁或需双向 → WebSocket / SSE”的原则做决策。赞分享文档/教程前端【免费下载链接】en.javascript.infoModern JavaScript Tutorial项目地址https://gitcode.com/gh_mirrors/en/en.javascript.info点击查看免费下载相关推荐JavaScript教程深入理解长轮询(Long Polling)技术JavaScript教程深入理解长轮询 Long Polling 技术 什么是长轮询 长轮询 Long Polling 是一种简单而有效的服务器 客户端通信技Egg.js消息推送终极指南WebSocket与HTTP长轮询实战对比Egg.js消息推送终极指南WebSocket与HTTP长轮询实战对比 Egg.js作为基于Node.js和Koa的企业级框架为实时消息推送提供了强大支持。后端Web框架告别轮询Alamofire WebSocket实现实时消息推送的完整指南告别轮询Alamofire WebSocket实现实时消息推送的完整指南 在移动应用开发中实时消息推送是提升用户体验的关键功能。传统的轮询方式不仅效率低下网络通信后端上一篇Video2X用AI超分把模糊老视频拉到4K下一篇SilentPatchGTA III、罪恶都市、圣安地列斯的 Win10/Win11 兼容性修复指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表