socket.io 使用多个 node 实例

2018-10-16 19:07 更新

棘手的负载均衡


如果要在不同的进程或主机之间进行连接负载的分发,必须要确保带有特定 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 进程传递消息,可以看看“使用外部消息源”.


以上内容是否对您有帮助:
在线笔记
App下载
App下载

扫描二维码

下载编程狮App

公众号
微信公众号

编程狮公众号