如何在PM2 Cluster模式下使用Socket.IO?求兼容示例代码
解决Socket.io与PM2集群模式兼容的问题
我之前也踩过这个坑!PM2集群模式下每个Worker都是独立的Node进程,内存完全隔离,而Socket.io默认的内存适配器没法跨进程共享连接状态——当客户端请求打到不同Worker时,Socket.io根本识别不了已有的连接,自然就会出现你遇到的NaN这类异常响应。
要解决这个问题,核心是用共享存储适配器让所有Worker同步Socket.io的状态,最常用的就是Redis适配器。下面给你一套完整的可运行示例:
第一步:安装依赖
先把需要的包装全:
npm install socket.io @socket.io/redis-adapter redis pm2 express
注:如果你的Socket.io是v3及以下版本,用
socket.io-redis替代@socket.io/redis-adapter即可。
第二步:编写服务器代码(server.js)
这里用Express+Socket.io,配置Redis适配器实现跨进程状态共享:
const express = require('express'); const http = require('http'); const { Server } = require('socket.io'); const { createAdapter } = require('@socket.io/redis-adapter'); const { createClient } = require('redis'); const app = express(); const server = http.createServer(app); // 初始化Redis客户端(主从客户端,适配Socket.io的适配器要求) const pubClient = createClient({ url: 'redis://localhost:6379' }); const subClient = pubClient.duplicate(); // 连接Redis Promise.all([pubClient.connect(), subClient.connect()]).then(() => { // 创建Socket.io实例并绑定Redis适配器 const io = new Server(server, { cors: { origin: "*", // 测试环境放开,生产环境要指定具体域名 methods: ["GET", "POST"] } }); io.adapter(createAdapter(pubClient, subClient)); // 监听Socket连接 io.on('connection', (socket) => { console.log(`Client connected: ${socket.id} (Worker: ${process.pid})`); // 示例:处理客户端请求并返回数据 socket.on('get-data', (callback) => { // 这里可以替换成你的业务逻辑 const responseData = { value: Math.floor(Math.random() * 100) }; callback(responseData); // 确保用回调返回,Artillery能正确捕获响应 }); socket.on('disconnect', () => { console.log(`Client disconnected: ${socket.id}`); }); }); // 启动HTTP服务器 const PORT = process.env.PORT || 3000; server.listen(PORT, () => { console.log(`Worker ${process.pid} listening on port ${PORT}`); }); }).catch(err => { console.error('Redis connection failed:', err); process.exit(1); });
第三步:配置PM2集群模式(ecosystem.config.js)
创建PM2的配置文件,指定集群模式和实例数:
module.exports = { apps: [ { name: 'socket-io-cluster', script: './server.js', instances: 'max', // 自动启动与CPU核心数一致的Worker exec_mode: 'cluster', // 启用集群模式 env: { NODE_ENV: 'development', PORT: 3000 }, env_production: { NODE_ENV: 'production', PORT: 3000 } } ] };
第四步:编写Artillery测试配置(artillery.yml)
确保测试脚本正确调用Socket.io的事件:
config: target: "http://localhost:3000" phases: - duration: 10 arrivalRate: 10 socketio: transports: ["websocket"] # 指定用WebSocket传输 scenarios: - name: "Test Socket.io get-data endpoint" engine: "socketio" flow: - emit: channel: "get-data" response: - channel: "get-data" match: value: "{{ value }}" capture: json: "$.value" as: "responseValue" - log: "Received response: {{ responseValue }}"
运行步骤
- 先启动Redis服务(确保本地Redis在6379端口运行,或者修改代码里的Redis地址)
- 启动PM2集群:
pm2 start ecosystem.config.js
- 运行Artillery测试:
artillery run artillery.yml
这样测试下来,你应该就能正常获取响应,不会再出现NaN的问题了。核心就是通过Redis适配器让所有PM2 Worker共享Socket.io的连接和事件状态,不管请求打到哪个Worker,都能正确处理客户端的连接和请求。
内容的提问来源于stack exchange,提问作者Bhuvanesh Arasu
相关产品推荐
相关产品推荐

