news 2026/10/21 19:54:42

WebSocket 配置 wss 访问:Nginx 反向代理、TLS 证书与心跳避坑指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
WebSocket 配置 wss 访问:Nginx 反向代理、TLS 证书与心跳避坑指南

简介:在Spring Boot 2.1中配置WebSocket并启用wss访问的完整工程示例,面向需要为聊天室、实时推送、在线协作或股票行情等场景增加加密通道的Java后端开发者。它直接解决浏览器对明文WebSocket的限制,以及HTTP服务在安全要求下无法直接升级为wss的问题,让实时双向通信能够运行在HTTPS之上。压缩包共66个文件,整体约61KB,里面既有Java源码、XML配置、jks证书文件、properties属性配置,也包含Maven包装脚本和完整的Git仓库元数据,目录结构清晰,便于对照自身项目调整。该资源已有13111人学习下载,在同类配置案例中具备较高参考热度。示例从依赖引入开始,依次演示keytool生成自签名证书、Spring Boot的SSL连接器配置、WebSocket处理器注册、前端JavaScript连接wss地址的完整链路,同时兼容SockJS,并针对证书配置错误给出了排错思路;读者可将其快速迁移到自己的HTTPS项目中,也能通过Git历史回溯每次配置改动,从而深入理解WebSocket与TLS握手背后的原理。

1. 为什么 ws:// 一部署就翻车:WebSocket 配置 wss 访问要解决的三个先决问题

每次把 WebSocket 服务从开发机搬到服务器,都会收到同一类求助:前端控制台飘红,后端日志却干净得可疑。多数时候问题出在协议上——页面已经变成 HTTPS,代码里还写着 ws://。浏览器的混合内容安全策略会直接拒绝这种连接的建立,业务逻辑还没执行,请求就死在了第一跳上。WebSocket 配置 wss 访问,核心价值是把这条链路的四个环节理清楚:TLS 终结放在哪、反向代理怎么写升级转发、证书链怎么接、超时与心跳怎么设置。这篇笔记适合部署长连接服务的后端开发与运维,你不需要读完整的协议说明,照着下面的配置和排查顺序,就能把一条 wss 链路部署上线并确认它能长期稳定运行。

2. WS 换成 wss 的本质:协议升级、TLS 终结与反向代理的选型逻辑

2.1 浏览器为什么拒绝 ws:安全上下文与混合内容策略

WebSocket 在浏览器里其实不是“随便连一个 TCP 端口”那么简单。页面通过 HTTPS 加载时,浏览器把这个页面放在一个安全上下文里;此时再发起 ws:// 连接,属于主动混合内容,因为页面本身是加密的,而这条 WebSocket 链路却是明文。浏览器的态度很明确:宁可连接失败,也不让你在加密页面里偷偷跑明文通道。我见过很多项目在这个阶段踩坑,报错文案常常是Failed to construct 'WebSocket',后面跟着一句“Please use 'wss://' scheme”,有的旧版本浏览器甚至只给一个红色错误,连原因都不提示。

另一个容易被忽略的风险在公网链路上。ws:// 的流量在传输过程中是明文,当 WebSocket 帧里携带的是在线消息、客服会话或者设备轨迹这类高频数据时,中间链路完全有能力记录和改写内容。wss:// 解决的正是浏览器到 TLS 终结点之间的传输安全问题,它不需要应用层再做一次加密,只要跑在 wss 通道里,握手之后的所有帧都是密文传输。

从协议设计看,wss 并没有发明一套新协议,它复用了 WebSocket 的握手流程,只是把传输层套进了 TLS。所以浏览器开发者工具里看到的 wss 请求,依然是一个带Upgrade: websocket头的 HTTP GET 请求,证书验证、SNI 等流程和 HTTPS 完全一致。搞清楚这一点,就不会在配置时把 wss 当成某种独立协议去折腾了。

2.2 一次 wss 握手发生了什么:GET、Upgrade 与 101 Switching Protocols

WebSocket 的握手在 HTTP 语义里只是一个 GET 请求,头部带了三个关键信息:Upgrade: websocket、Connection: Upgrade、Sec-WebSocket-Key。服务端如果接受这个升级请求,会返回101 Switching Protocols,随后这条 TCP 连接直接变成 WebSocket 数据通道,不再按 HTTP 请求一问一答。之后两边就是双工通信,谁都可以主动发数据,控制面和数据面都在同一条连接里。

问题往往出在反向代理这一层。常规的 HTTP 代理会丢弃 Upgrade 头部,因为这种东西在普通 HTTP 请求里没有意义;更严格的一些安全设备还会拦掉Connection: Upgrade。所以 wss 配置的核心动作,就是让 Nginx 能把这类请求识别出来、原样转发,并且在握手完成后保持连接长驻,不能让代理把这条连接当成普通 HTTP 请求来处理。

这里有个实际的部署结论:如果当前是用 TCP 四层转发直接到达后端 WebSocket 服务,TLS 终结要么在后端做,要么由四层负载均衡整体透传。此时后端代码里如果做了基于来源地址或来源协议的判断,拿到的都是负载均衡的地址,排查问题时很容易绕弯路。这也是我倾向于把 wss 收敛到一个七层网关上的原因,Nginx 对 WebSocket 升级的支持已经很成熟,没必要自己维护一套 TLS 转发。

2.3 TLS 终结放在哪一层:反向代理与应用服务直挂证书

“TLS 终结”指的是 TLS 加密握手到此结束,之后的流量变成内部协议。常见做法有两个方向:反向代理终结和应用服务终结,我列出对比供选型时参考。

对比维度反向代理终结应用服务终结
证书管理在网关统一管理,多服务共用一张证书每个服务单独配置证书与续期
后端改造量后端保持 ws:// 监听,代码基本不动框架需要配置 TLS 路径与版本
排查难度后端本地就能用 ws:// 直接抓包调试线上与本地网络环境差异较大
适用场景多服务共用域名、端口的前后端分离架构边缘计算、嵌入式、独立网关部署

我的默认选择是反向代理终结。理由很直接:长连接服务数量一多,证书续期集中在网关一处,不会因为某个后端服务忘了续期让全局 wss 失联;后端可以继续用 ws:// 监听,本地开发环境也不需要复刻一套正式证书。反向代理终结并不是没有代价,它引入了一个代理层,空闲超时、读取超时都要和业务心跳机制对齐,多实例部署时还得处理会话粘连问题,这两点正是后面避坑章节的主要内容。

规模很小的项目也可以让后端直接终结 TLS,少一层转发。但为了省掉一跳去牺牲证书统一管理,我一般不建议这么干,尤其是还有 Web 页面要共用一个域名的时候。

2.4 路径与端口规划:443 一条路还是单独端口吃流量

路径规划几乎决定后续所有配置写法。如果 WebSocket 和 HTTPS 页面共用同一个域名,通常走wss://域名/ws这样的统一前缀,和普通 API 共享 443 端口。这样做的好处很明显:证书只需一份,防火墙只需要放行 443,浏览器也不会因为跨端口产生额外的同源判断问题。

也有人习惯单独开一个端口,比如 8081 或 8443 专门给 WebSocket 用。这会让流量审计更清晰,但代价也明显:安全组要额外放行端口,部分企业网络会拦截非标准端口的长连接,用户连不上以后还很难定位。另开端口只适合后端集群完全独立、需要按端口做流量隔离的场景。

我在配置时习惯给 WebSocket 单独划一个路径前缀,比如/socket,避免和业务 API 的/api路径混在同一个 location 里。这样做的另一个好处是,后续要调整心跳、限流或者校验逻辑时,只改这个 location,不牵连普通 HTTP 路由。如果你要兼容多个端,比如 App 端和 Web 端走不同握手参数,路径前缀拆开以后也好做差异化处理。

3. 用 Nginx 配置 wss:证书准备、升级转发与超时参数的落地写法

3.1 证书准备:自签调试与正式证书的文件组织

wss 连接能不能建立,一半取决于证书链。内网或者本地调试阶段没必要为每个测试环境申请正式证书,用 OpenSSL 生成一张自签证书就够了:

openssl req -x509 -newkey rsa:2048 \ -keyout /etc/nginx/cert/ws.example.com/key.pem \ -out /etc/nginx/cert/ws.example.com/cert.pem \ -days 365 -nodes \ -subj "/CN=ws.example.com"

参数含义:

  • req -x509:生成自签证书,跳过 CSR 申请流程,适合内网验证。
  • 2048:RSA 密钥位数,现在不建议低于 2048。
  • days 365:有效期一年,自签证书建议短周期,到期前记得重新生成。
  • -nodes:私钥不设密码,启动 Nginx 时不用手工输入,方便自动化重启。
  • CN:填写域名而不是 IP。如果要在浏览器里直接访问 IP 的 wss,还需要在生成时加 SAN 扩展,否则证书校验过不了。

正式环境建议按域名建目录组织文件,避免多套服务混在一起:

/etc/nginx/cert/ws.example.com/ ├── cert.pem ├── chain.pem ├── fullchain.pem └── key.pem

上面fullchain.pem是站点证书加上中间证书的合并文件,Nginx 的ssl_certificate应该指向它而不是只填站点证书。证书链缺失的症状很难一眼定位,桌面浏览器可能一切正常,手机端弱网或特定客户端直接报证书错误,这个问题会在避坑章节里再展开。

3.2 一份可以直接改域名使用的 Nginx 配置

把 WebSocket 服务接到 wss 访问,常见做法是走 Nginx 七层转发。下面是一份最小可用且能直接复制的配置,只保留了与 WebSocket 相关的部分:

# 放在 http{} 顶层,负责把客户端 Connection 头映射成合适的值 map $http_upgrade $connection_upgrade { default upgrade; '' close; } upstream ws_cluster { server 127.0.0.1:8080; keepalive 32; } server { listen 443 ssl; server_name ws.example.com; ssl_certificate /etc/nginx/cert/ws.example.com/fullchain.pem; ssl_certificate_key /etc/nginx/cert/ws.example.com/key.pem; ssl_session_cache shared:SSL:10m; ssl_session_timeout 1h; location /ws { proxy_pass http://ws_cluster; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_connect_timeout 15s; proxy_read_timeout 60s; proxy_send_timeout 60s; } }

关键行说明:

  • map块负责把$http_upgrade映射成$connection_upgrade。客户端请求里带了Upgrade: websocket时,转发给后端的Connection头是upgrade;如果只是普通 HTTP 请求,则映射为close,不会误伤常规 API 连接。
  • proxy_http_version 1.1是 wss 配置里最容易漏的一行。WebSocket 握手依赖 HTTP/1.1 的持久连接语义,如果仍然使用默认的 1.0,后端会直接拒绝升级。
  • proxy_set_header Upgrade与Connection两行必须配合 map 使用,绝不能把 Connection 写死为upgrade,否则所有经过这个 location 的普通 HTTP 请求也会被强制设为长连接,连接数很快被打爆。
  • keepalive 32让 Nginx 与后端之间保持一批复用连接,减少频繁握手造成的延迟和 TIME_WAIT 堆积。这个指令只有在proxy_http_version 1.1时才有意义。
  • proxy_read_timeout对长连接的意义最大。它表示从后端读取数据的空闲超时时间,默认 60 秒。如果业务心跳周期超过 60 秒,连接会被 Nginx 断开,这就是很多线上 wss“准时断线”的来源。

如果 WebSocket 路径不是/ws,比如是/socket.io,只需把location /ws改成对应前缀即可。注意proxy_pass后面不要带 URI 路径,否则 Nginx 会对 location 匹配到的部分做路径重写,后端实际收到的路径可能比预期少一段,导致路由 404。

3.3 把真实客户端信息传给后端:X-Forwarded-* 头的作用

配置里那三行X-Forwarded-For、X-Real-IP、X-Forwarded-Proto在日常开发中常常被忽略,但一旦要做按 IP 限流或者按协议判断来源,它们就发挥作用了。WebSocket 握手发生在 HTTP 请求阶段,这些头在握手时已经完整传给后端;后端在收到 Upgrade 请求时读取这些头,就能拿到真实客户端 IP,而不是反代的内网地址。

需要注意,直接把前端传来的X-Forwarded-For当真实 IP 是有风险的。更稳妥的做法是在 Nginx 层用real_ip模块,把可信来源的X-Forwarded-For替换成$remote_addr,再统一写入转发头。如果后端代码里写好了取 IP 的逻辑,也要检查它取的是握手阶段的信息,而不是等连接建立后再从 socket 上拿,因为 WebSocket 建立以后根本没有 HTTP header 可读。

X-Forwarded-Proto的作用是告诉后端原始请求是 http 还是 https。后端拿到这个头才知道页面是安全的,从而决定构造用于回跳的 URL 时用https://还是http://,否则很容易生成一个错误的 http 链接,又引发一轮混合内容报错。

3.4 别让防火墙在最后一米拦截:端口与安全组检查

配置完成后,第一个动作不是打开前端页面,而是确认网络路径上的端口是通的。443 端口在多数情况下已放行,但如果你的 wss 用了非标准端口,就要在安全组和防火墙两侧都加放行规则。可以这样检查本机监听状态:

ss -lnt | grep -E ':(443|8080)\b'

如果 443 端口正常监听,再用另一台机器用连接工具测 TCP 通断。如果端口通但握手失败,问题在 Nginx 配置;如果端口都不通,优先检查安全组规则,而不是反复折腾证书。另一个常见现象是 Nginx 配了 443,后端服务也在同一台机器监听 443,端口冲突导致 Nginx 启动失败。ss -lnt输出里同一个端口只能被一个进程监听,看到两个进程争抢时先处理端口占用。

4. 验证 wss 是否真的通了:浏览器、curl 与 Node 客户端的三层检查

4.1 浏览器控制台快速冒烟:先看 readyState 再看状态码

配置生效后的第一关是浏览器。打开业务页面,进入开发者工具,切到 Network 面板下的 WS 标签,过滤所有 WebSocket 请求,然后直接执行一段冒烟代码:

// 浏览器控制台直接粘贴 const ws = new WebSocket('wss://ws.example.com/ws'); ws.onopen = () => console.log('open:', ws.readyState); ws.onerror = e => console.error('error:', e.message); ws.onclose = ev => console.log('close:', ev.code, ev.reason);

判断要点:

  • readyState为 1 表示握手成功,连接可用。0 表示正在连接,2 表示正在关闭,3 表示已关闭。
  • onerror里的事件对象信息有限,很多浏览器拿不到真正的报错原因。此时要到 Network 面板的 WS 请求里看状态码。
  • 状态码101 Switching Protocols是唯一正确的握手结果。看到 403,多半是跨域 Origin 校验失败;看到 400,是 Upgrade/Connection 头配置不对;看到 502,是 Nginx 连不上后端,检查 upstream 配置和后端进程。

浏览器验证的好处是贴近用户真实环境,问题在于面板里不会展示证书链详情。所以只靠浏览器还不够,下面用 curl 和 Node 客户端把链路拆开验证。

4.2 用 curl 验证 TLS 和协议升级是否同时成立

curl 不能真正完成 WebSocket 数据帧收发,但用来验证握手阶段非常好用:

curl -i -N --insecure \ -H "Connection: Upgrade" \ -H "Upgrade: websocket" \ -H "Sec-WebSocket-Key: SGVsbG8sIHdvcmxkIQ==" \ -H "Sec-WebSocket-Version: 13" \ "https://ws.example.com/ws"

执行后重点看两处:响应头里的HTTP/1.1 101 Switching Protocols,以及Sec-WebSocket-Accept字段。只要看到 101,说明从 Nginx 到后端这条升级链路是通的。

参数说明:

  • -i输出响应头,-N禁止缓冲,连接建立后保持打开状态。
  • --insecure是临时跳过证书校验的开关。生产验证时不要带它,让 curl 走系统受信任证书,才能顺便检查证书链是否完整。
  • Sec-WebSocket-Key只需要是一个合法的 Base64 占位值,多数后端实现并不严格校验这个值本身;响应里的Sec-WebSocket-Accept才是服务端计算后的结果。
  • 如果 curl 返回 400,优先检查 Nginx 的proxy_http_version和 Upgrade/Connection 头。如果返回 502,说明后端连接不上,重点查 upstream 端口与后端服务状态。

4.3 完整收发一帧数据:wscat 与 Node.js 客户端

curl 只能证明握手成功,数据帧收发还得用完整客户端。我常用的工具是 wscat:

npm install -g wscat wscat -c wss://ws.example.com/ws

连接成功后,wscat 进入交互模式,可以直接输入文本发送给服务端,收到的响应也会实时打印。这个工具对排查“服务端有没有返回数据”非常直观,比写完整脚本快得多。

如果要把验证写进自动化流程,可以用 Node 的 ws 库:

const WebSocket = require('ws'); const ws = new WebSocket('wss://ws.example.com/ws', { rejectUnauthorized: false, // 仅自签测试环境使用,生产删除 headers: { Origin: 'https://your-page.example.com' // 模拟浏览器同源场景 } }); ws.on('open', () => { console.log('opened'); ws.send(JSON.stringify({ type: 'ping' })); }); ws.on('message', data => { console.log('message:', data.toString()); ws.close(); }); ws.on('close', (code, reason) => { console.log('closed:', code, reason.toString()); });

代码说明:

  • rejectUnauthorized: false只在自签证书的测试环境里开。生产环境必须略去该参数或者传入自定义 CA,否则测试结果会掩盖真实的证书链问题。
  • Origin头用来模拟浏览器行为。很多后端框架在 WebSocket 握手时会校验 Origin,不带这个头可能被拒绝。这里填的域名要和生产页面保持一致。

4.4 把握手验证固化进发布流程

部署完 wss 配置后,最容易被忽略的是“配置改好了但忘记 reload Nginx”这种情况。把握手验证写成一个脚本放入发布流程,能从源头规避这种低级事故:

code=$(curl -s -o /dev/null -w "%{http_code}" \ -H "Connection: Upgrade" \ -H "Upgrade: websocket" \ -H "Sec-WebSocket-Key: SGVsbG8sIHdvcmxkIQ==" \ -H "Sec-WebSocket-Version: 13" \ "https://ws.example.com/ws") if [ "$code" -ne 101 ]; then echo "WSS handshake failed: $code" exit 1 fi

这里用-s静默模式避免输出多余信息,-o /dev/null丢弃响应体,-w "%{http_code}"只提取状态码。发布脚本里拿到 101 才继续,否则直接失败,能拦截大部分配置漏 reload、证书路径写错、端口没放行一类问题。

5. wss 配置避坑记录:混合内容来源、证书链顺序、空闲超时与多实例粘连

5.1 现象:浏览器提示连接失败,Nginx 日志里什么都没有

这是一个非常有代表性的现象。前端页面用 HTTPS 打开,代码却照搬了开发环境的 ws:// 地址。浏览器在发出连接前就把请求拦掉了,所以服务端访问日志里根本不会出现这条请求。

原因:浏览器强制要求安全上下文里的 WebSocket 必须使用 wss 协议,ws:// 在 HTTPS 页面下被当作混合内容直接拒绝。

解决:不要在前端代码里写死协议,而是根据页面协议动态拼接:

const wsProto = location.protocol === 'https:' ? 'wss://' : 'ws://'; const ws = new WebSocket(`${wsProto}${location.host}/ws`);

这里用location.host而不是location.hostname,目的是把端口也带上,避免页面运行在非 443 端口时,WebSocket 请求打到错误的端口上。开发环境用 http 时自动走 ws,线上 https 时自动走 wss,一套代码两端通用,后面再也不会因为协议写死而出问题。

5.2 现象:桌面浏览器正常,手机端却提示证书无效

桌面端浏览器对证书链的宽容度较高,但部分手机浏览器或旧客户端对证书链要求更严格。表现是 wss 握手卡住,重试多次仍失败。

原因:Nginx 的ssl_certificate只配置了站点证书,没有把中间证书合并进去。客户端只信任它本地内置的根证书,缺少中间证书时无法完成信任链验证。

解决:把站点证书和中间证书按顺序合并成一份文件,顺序必须是“站点证书在前,中间证书在后”:

cat ws.cert.pem chain.pem > fullchain.pem

合并后用 openssl 检查服务端证书链:

openssl s_client -connect ws.example.com:443 -servername ws.example.com </dev/null | grep verify

看到verify return code: 0 (ok)说明证书链完整。如果看到unable to get local issuer certificate,说明中间证书还是没发出去,继续检查合并文件内容,不要只对比文件名是否存在。

5.3 现象:wss 连接能建立,但约 60 秒后准时断开

这是部署 wss 时最高频的线上问题,用户反馈通常是“连上之后一分钟左右就掉线,重连以后也只能撑一分钟”。

原因:Nginx 的proxy_read_timeout默认值是 60 秒。当 WebSocket 连接处于空闲状态、没有数据帧经过代理时,Nginx 会把连接判定为超时并主动关闭。如果业务应用没有做心跳保活,断线时间会非常精确地落在 60 秒附近。

解决:从两个方向入手。先调整代理超时,让代理层与业务心跳周期对齐:

location /ws { proxy_read_timeout 75s; proxy_send_timeout 60s; }

更根本的措施是给应用加上心跳帧。WebSocket 协议本身就支持 ping/pong 帧,前端或后端每 25 到 30 秒发送一个 ping,对端返回 pong,代理层会持续看到数据流动,不会触发空闲超时。只调大代理超时是治标不治本,长连接服务一旦没有心跳,遇到负载均衡节点切换或网络抖动时仍然会被静默断开。

5.4 现象:单实例正常,多实例部署后 wss 频繁掉线

如果后端服务从一台扩到多台,wss 连接反而开始不稳定,浏览器反复重连,每次重连只能撑几秒到几十秒不等。

原因:WebSocket 是有状态的连接,连接建立后所有消息都在这条 TCP 通道上传输。负载均衡把握手请求分到实例 A,后续数据帧却被算法分到实例 B,两个实例各自维护的连接状态不同步,消息自然传递不过去。

解决:思路有两类。第一类是让网关做会话粘连,把同一个连接固定在同一个后端实例上。Nginx 的ip_hash对固定出口的办公网络简单有效,但移动网络会频繁切换出口 IP,ip_hash 不一定稳定;更通用的是启用负载均衡的会话保持或 cookie 粘滞能力。第二类是后端把连接会话集中管理,比如把连接 ID 与节点映射放到共享缓存,收到消息后转给持有对应连接的节点处理。这个方案改造量稍大,但在多实例架构里最可靠。

5.5 现象:握手返回 403,业务组件看起来都正常

浏览器 Network 面板里能看到这条 wss 请求,但状态码是 403,服务端日志提示不是一个被允许的来源。

原因:服务端框架对 WebSocket 握手做了同源校验,握手请求里的 Origin 头不在允许列表。这个问题和 Nginx 无关,是后端业务代码的跨域白名单没有覆盖生产页面域名。

解决:把生产页面域名加进服务端允许来源配置。比如页面部署在https://your-page.example.com,就把这个域名加到 WebSocket 服务的跨域白名单中。调试时可以用上一章 Node 客户端的 Origin 头模拟这个来源,不需要每次改完都去页面刷新验证。要注意 Origin 头里带的是协议加域名加端口,配置白名单时尽量精确到这种完整格式,避免为了省事用通配符放开所有来源。

6. 生产环境最后一公里:wss 心跳节奏、负载均衡连接与断线重连

6.1 让心跳、代理超时和负载均衡空闲阈值保持同一节奏

wss 配置一旦跑起来,长期稳定性靠的是对齐三组时间:应用心跳间隔、反向代理读超时、负载均衡空闲阈值。三者的理想关系是“心跳间隔最小,代理超时居中,负载均衡空闲阈值最大”。我常用的基准值如下:

项目推荐值
心跳间隔25 秒
Nginx read_timeout75 秒
前端重连初始延迟1 秒
前端重连最大延迟30 秒

心跳间隔不建议压到 10 秒以内,过于密集的 ping 帧会浪费移动网络电量和带宽;比 25 秒更长则容易逼近网关空闲阈值。如果负载均衡的空闲超时无法调整,就把心跳间隔压到该阈值的二分之一甚至三分之一。

6.2 断线重连的正确姿势:指数退避与关闭码区分

很多前端的重连逻辑就是简单写一个固定延迟的setTimeout(reconnect, 3000),服务端一重启,几千个客户端在同一秒发起 wss 握手,网关和后端同时承压。正确的姿势是加指数退避:

let retryDelay = 1000; const maxDelay = 30000; let manualClose = false; function connect() { const ws = new WebSocket('wss://ws.example.com/ws'); ws.onopen = () => { retryDelay = 1000; // 成功后重置重试延迟 }; ws.onclose = (e) => { if (manualClose) return; setTimeout(connect, retryDelay); retryDelay = Math.min(retryDelay * 2, maxDelay); }; } connect();

这段代码做了三件事:成功后把等待时间复位、断线后延迟翻倍、限制最大延迟。关键点是manualClose这个标志位——当页面主动关闭连接或用户退出登录时,要把manualClose置为 true,否则用户退出后还会无限重连骚扰服务器。另外,关闭码也很重要,收到 1008 这类策略拒绝码说明业务层面不允许连接,这时候不应该继续重连,而是提示用户重新登录。我的习惯是把关闭码分成“可自动重连”和“需要人工介入”两类,而不是所有 close 都走同一套延迟策略,这样既保证恢复速度,又不会在业务拒绝时制造海量无效连接。

到现在,wss 的部署才真正从“能连上”变成“能长期跑”。我每次上线长连接服务,都会把握手验证、心跳节奏、断线重连三项过一遍检查单,这三项帮我挡住了绝大多数用户在凌晨才发现的事故。希望这份配置和排查顺序能帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/14 13:56:34

Node.js大文件哈希计算优化:流式读取为何比一次性读入更快

先说结论&#xff1a;在Node.js里用crypto.createHash给大文件算哈希&#xff0c;流式喂数比一次性喂数快&#xff0c;不是玄学&#xff0c;它让内存占用、垃圾回收和I/O调度这三件事同时变好了。前几天做一个对象存储入库前的SHA-256校验工具&#xff0c;要处理一批几十GB的光…

作者头像 李华
网站建设 2026/10/13 15:03:31

JSP家庭作业管理系统:从源码到部署的Java Web实战解析

JSP家庭作业管理系统&#xff1a;从源码到部署一次讲透这段时间我在梳理一个JSP家庭作业管理系统&#xff0c;从数据库设计到页面渲染&#xff0c;再到Tomcat底下调通整个链路&#xff0c;过程相当折腾&#xff0c;也相当过瘾。这个项目把Java Web开发最经典的组件挨个用了一遍…

作者头像 李华
网站建设 2026/10/14 16:27:25

JMeter二次开发实战:自定义函数解决动态签名与参数处理

压测做久了&#xff0c;你会发现真正折磨人的往往不是并发量&#xff0c;而是请求参数本身。JMeter自带的那套函数库&#xff0c;平时用着还行&#xff0c;一旦遇到“每个请求都要动态算签名”“按业务规则生成一批特定手机号”“从响应里取出来的值还得做二次编码”这类场景&a…

作者头像 李华
网站建设 2026/10/16 8:08:55

5个轻量级Pi开源工具:提升开发者日常效率的终端瑞士军刀

1. 项目概述&#xff1a;为什么这5个Pi开源扩展值得你花30分钟装上“Pi”这个字眼在当前技术圈里&#xff0c;已经悄然脱离了数学常数的单一语境&#xff0c;演变成一个轻量、可嵌入、面向边缘与个人开发者的工具代称——它不指代某款硬件&#xff0c;而是一类以极简架构、低资…

作者头像 李华
网站建设 2026/10/20 22:24:12

Codex一直显示“正在思考”?WebSocket握手失败与HTTPS回退全解析

最近被 Codex 折腾了一把——对话框里一直显示“正在思考”&#xff0c;转圈转到天荒地老&#xff0c;既没有报错也没有结果。这不是网络慢的问题&#xff0c;排查到最后发现&#xff0c;真正的原因藏在 WebSocket 连接上&#xff1a;客户端尝试用 WebSocket 做流式请求&#x…

作者头像 李华
网站建设 2026/10/17 18:39:55

MediaGo流媒体下载:从HLS分段解析到30MB/s极速保存

流媒体时代&#xff0c;很多人都有这样的经历&#xff1a;刷到一个收藏级的公开课视频&#xff0c;看完一遍不过瘾&#xff0c;想转存到本地慢慢品&#xff1b;或者自己付费订阅了某个在线学习平台&#xff0c;会员到期之前想把课程备份下来&#xff0c;却发现平台只给了"…

作者头像 李华