PM2 下 Session ID unknown 怎么解决

PM2 cluster 部署 Socket.IO 时出现 Session ID unknown、WebSocket 升级失败或连接时断时连,先确认一次连接中的握手、轮询和 Upgrade 请求是否被分到了不同 worker。只要客户端还允许长轮询回退,就需要按连接配置粘性会话;如果只允许 WebSocket 传输,则可以评估不使用粘性会话。

排查时不能只在 Nginx 中添加 ip_hash 后结束检查。Nginx 若只代理一个由 PM2 cluster 共用的端口,它看不到后面的 worker,粘性路由仍要由 PM2 侧解决;只有把各实例拆成独立本地端口后,Nginx 才能在 upstream 中固定到某个实例。

PM2 把一次连接拆散后,报错从哪里来

Socket.IO 并不是一条裸 WebSocket 连接。底层 Engine.IO 负责 HTTP 握手、连接升级、心跳以及 polling 等传输过程,因此建立一次连接可能涉及多次 HTTP 请求。

这些后续请求会携带当前连接的 sid。如果 PM2 把请求分配给另一个 worker,而该 worker 的内存中并不存在这个会话,服务端就无法识别 sid,于是出现 Session ID unknown。如果连接升级阶段也落到其他进程,客户端还可能表现为 WebSocket 握手失败;使用 polling 回退时,则会表现成连接建立后时断时连。

所以,这类问题通常不是 Socket.IO 本身突然失效,而是同一条连接被 PM2 拆散后,各段请求命中了不同进程。排查时应先记录实际使用的传输方式,再检查握手、轮询、心跳和 Upgrade 分别落到了哪个 worker。

粘性会话应该加在 PM2 还是 Nginx

如果使用 PM2 cluster 的共享端口模式,可以按 Socket.IO v4 官方 PM2 文档核对 @socket.io/pm2 方案:在应用启动时调用 setupMaster({ sticky: true }),再启动多个 worker。官方文档对应的是 Socket.IO v4,项目中的 PM2、Socket.IO 与该模块版本是否匹配,需要按实际依赖自行确认。

启动命令可以写成:

pm2 start app.js -i max --name socket.io-app

其中脚本名和应用名需要按项目替换;-i max 用于按可用 CPU 核心数启动多个实例,--name 则用于标识对应的进程组。

另一种拓扑是不让实例共享端口,而是让它们分别监听 127.0.0.1:3001、127.0.0.1:3002 等本地端口,再由 Nginx 的 upstream 统一转发。此时可以在 upstream 中使用 ip_hash,同时转发 WebSocket Upgrade 请求。

upstream socket_io {
    ip_hash;

    server 127.0.0.1:3001;
    server 127.0.0.1:3002;
}

server {
    listen 80;
    server_name socket.example.com;

    location / {
        proxy_pass http://socket_io;

        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

ip_hash 根据客户端源 IP 将请求固定到某个 upstream 实例,并不是根据 Socket.IO 的 sid 粘住连接。因此,多个客户端如果经过同一个 NAT 或出口地址,可能集中到同一个实例;它也不能替你选择 PM2 共享端口后面的具体 worker。 如果 Nginx upstream 中只有一个 PM2 cluster 共享端口,仍应回到 PM2 侧检查粘性配置。

如果只使用 WebSocket 传输,可以不使用粘性会话;Socket.IO v4 官方文档给出了这一结论,但客户端传输参数需要按项目使用的版本核对。

配置后怎么确认连接没有跳 worker

验证时不要只看浏览器是否显示“已连接”。应在握手、连接建立和断开事件中记录 process.pid、实例监听端口、实际传输方式以及必要的 sid 信息,然后核对同一个客户端后续的 polling 请求和 Upgrade 是否命中同一个进程。 使用 PM2 共享端口时,以 worker PID 为准;使用多个本地端口时,以实例端口为准。

测试至少要覆盖实际使用的传输方式。若启用了长轮询回退,只验证 WebSocket 升级成功会漏掉一部分路由问题;若使用 ip_hash,还应从不同客户端来源检查 upstream 分布,避免把“多个客户端落在同一出口 IP”误判成粘性会话失效。

粘性会话解决的是连接路由,不等于多个节点的 Socket 已经合并。多个进程或节点上的连接如何互相广播,是另一项需要单独设计的问题,现有讨论也明确记录了多实例连接无法直接合并的情况。 因此,Session ID unknown 消失后,跨节点消息、负载分布、版本兼容和故障切换仍要按实际拓扑验证,不能仅凭连接建立成功判断整个集群已经正常。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容