socket.io 使用多个 node 实例
棘手的负载均衡
如果要在不同的进程或主机之间进行连接负载的分发,必须要确保带有特定 session id 的请求连接到产生该 id 的进程。
这是由于特定的传输方式(比如 XHR 轮询或 JSONP 轮询)依赖于在 “socket” 的声明周期中发送多个请求。
要阐明这么做的原因,可以参考下面的例子。这个例子是要在所有连接到服务器的客户端上触发一个事件:
io.emit('hi', 'all sockets');
可能有些客户端使用的是双向通信机制比如 WebSocket
,这样我们可以立即向其中写入消息。但是可能还有一些使用的是长轮询(long-polling)机制。
如果客户端使用了长轮询,它们不一定发送了可以向其中写入消息的请求。它们可能处于这些请求之间的阶段。这种情况下,我们需要在进程中缓存消息。为了让客户端能在发送请求时取回这些消息,最简单的方法就是让客户端连接到同一个进程上。
一种简单的方式是使用客户端的原始地址来进行路由。下面的例子使用了 NginX 服务器:
NginX 配置
在 nginx.conf
文件的 http { }
块中,可以声明一个 upstream
块,里面声明一个需要进行负载均衡的 Socket.IO 进程列表:
upstream io_nodes {
ip_hash;
server 127.0.0.1:6001;
server 127.0.0.1:6002;
server 127.0.0.1:6003;
server 127.0.0.1:6004;
}
注意 ip_hash
指令表明这些连接是持久的。
同样在 http { }
块中,还可以声明一个 server { }
来指向这个 upstream。为了让 NginX 支持并转发 WebSocket
协议,可以明确传递必需的 Upgrade
消息头:
server {
listen 3000;
server_name io.yourhost.com;
location / {
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_http_version 1.1;
proxy_pass http://io_nodes;
}
}
请确保在最顶层配置了 worker_processes
参数,来指定 NginX 使用的工作进程数量。你还可以在 events { }
块中调节 worker_connections
设置。
使用 Node.JS 集群
和 NginX 一样, Node.JS 通过 cluster
模块提供了内置的集群支持。
Fedor Indutny 创建了一个 sticky session 模块。该模块确保文件描述符(也就是连接)可以通过来源的 remoteAddress
(也就是 IP)进行路由。
在 node 实例之间传递事件
假如有多个 Socket.IO 的 node 实例同时提供服务,而你要广播事件给所有人(或者特定房间里的所有人),你需要某种机制来在进程或主机之间传递消息。
这种消息路由的接口我们称之为 Adapter
(适配器)。你可以在 socket.io-adapter 之上实现自己的适配器(通过继承),或者直接使用我们提供的基于 Redis 的适配器: socket.io-redis:
var io = require('socket.io')(3000);
var redis = require('socket.io-redis');
io.adapter(redis({ host: 'localhost', port: 6379 }));
如果你需要从非 socket.io 进程传递消息,可以看看“使用外部消息源”.
更多建议: