多Socket.io服务器部署与Express跨服务会话共享方案问询
Great question! Building a multi-Socket.io server setup like csgohunt.com (with separate instances for real-time game data, chat, and main site interactions) requires solving three core challenges: isolating Socket.io responsibilities across servers, enabling inter-server communication, and sharing user sessions across different HTTP/Socket.io instances. Let me break this down with practical steps and code examples.
First, you’ll want to separate your Socket.io instances into distinct services (like ws.csgohunt.com for game-related real-time data, chat.csgohunt.com for messaging, and the main csgohunt.com for site-wide events). Each can be a standalone Express + Socket.io app deployed on separate HTTP servers (VPS, containers, or Kubernetes pods).
This separation helps with:
- Performance: Isolating high-traffic workloads (like chat) from critical game data streams
- Maintainability: Each team can own their service without impacting others
- Scalability: Scale individual servers independently based on demand
By default, Socket.io uses an in-memory adapter, which means events only stay within a single server. To let your three Socket.io instances communicate with each other (e.g., notify chat users when a game event happens), you’ll need a shared adapter—Redis Adapter is the standard solution here.
Here’s how to configure it for any Socket.io server:
const { createServer } = require('http'); const { Server } = require('socket.io'); const { createAdapter } = require('@socket.io/redis-adapter'); const { createClient } = require('redis'); // Create HTTP server and Socket.io instance const httpServer = createServer(); const io = new Server(httpServer, { cors: { origin: "https://csgohunt.com", // Allow your main site to connect credentials: true // Enable cookie sharing } }); // Connect to Redis (shared across all Socket.io servers) const pubClient = createClient({ url: 'redis://your-redis-host:6379' }); const subClient = pubClient.duplicate(); // Initialize the adapter once Redis connections are ready Promise.all([pubClient.connect(), subClient.connect()]).then(() => { io.adapter(createAdapter(pubClient, subClient)); }); // Start the server httpServer.listen(3000);
With this setup, any event emitted on one Socket.io server will be broadcast to all connected clients across all servers, and servers can communicate directly via Redis.
The trickiest part is making sure all Socket.io servers know if a user is logged in. Here’s how to achieve shared sessions:
Step 3.1: Store Sessions in Redis (Not Memory)
Never use in-memory session storage for multi-server setups—instead, use Redis to store session data so all servers can access it. Configure your Express session like this:
const express = require('express'); const session = require('express-session'); const RedisStore = require('connect-redis').default; const { createClient } = require('redis'); const app = express(); const redisClient = createClient({ url: 'redis://your-redis-host:6379' }); redisClient.connect().catch(console.error); // Use Redis as the session store const redisStore = new RedisStore({ client: redisClient }); app.use(session({ secret: 'your-strong-secret-key', // Use a secure secret in production resave: false, saveUninitialized: false, store: redisStore, cookie: { secure: true, // Enable this if using HTTPS (production) httpOnly: true, sameSite: 'none', // Required for cross-subdomain cookie sharing domain: '.csgohunt.com' // Critical: Share cookies across all subdomains } }));
The domain: '.csgohunt.com' setting is key—it lets cookies set by csgohunt.com be accessible to ws.csgohunt.com and chat.csgohunt.com.
Step 3.2: Authenticate Socket.io Connections with Session Data
Socket.io handshake requests carry cookies, so you can parse these cookies, fetch the session from Redis, and verify the user’s login status before allowing the connection. Add this middleware to your Socket.io setup:
const cookieParser = require('cookie-parser'); io.use(async (socket, next) => { // Parse cookies from the handshake headers const cookies = cookieParser.JSONCookies(socket.handshake.headers.cookie || ''); const sessionId = cookies['connect.sid']; // Match your express-session cookie name if (!sessionId) { return next(new Error('No session found')); } // Fetch the session data from Redis const sessionData = await redisStore.get(sessionId); if (!sessionData || !sessionData.user) { return next(new Error('Unauthorized')); } // Attach user data to the socket for use in event handlers socket.user = sessionData.user; next(); });
Now every Socket.io connection across all servers will have access to the user’s session data.
To map subdomains like ws.csgohunt.com to your respective Socket.io servers, use a reverse proxy like Nginx. Here’s a sample configuration:
# Route ws.csgohunt.com to your game Socket.io server server { server_name ws.csgohunt.com; location / { proxy_pass http://your-ws-server-ip:3000; 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-Real-IP $remote_addr; } } # Route chat.csgohunt.com to your chat Socket.io server server { server_name chat.csgohunt.com; location / { proxy_pass http://your-chat-server-ip:3001; 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-Real-IP $remote_addr; } } # Route main site and its Socket.io endpoint server { server_name csgohunt.com; location / { proxy_pass http://your-main-server-ip:3002; } location /socket.io { proxy_pass http://your-main-server-ip:3002/socket.io; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; } }
- Scalability: As your user base grows, you can add more instances of each Socket.io server behind the reverse proxy—Redis will handle the inter-server communication and session storage seamlessly.
- Security: Always use HTTPS in production, keep your session secret secure, and validate all Socket.io connections.
- Containerization: For easier deployment, package each Socket.io server and Redis into Docker containers, then use Docker Compose or Kubernetes to manage them.
内容的提问来源于stack exchange,提问作者EmaMaMaso EmaSeMa

