socket-io集群解决方案

关于socket.io的群集解决方案,官网中给出了3个示例代码。除了负载均衡的软件不同,其他的代码其实都是一样的,分别为:nginx,httpd, haproxy。以下以nginx为例说明。官网给的demo基于docker,让你可以直接运行。代码结构如下:

socket.io集群结构

  • 首先代码index.js中加入redis适配
    var io = require('socket.io')(3000);
    var redis = require('socket.io-redis');
    io.adapter(redis({ host: 'localhost', port: 6379 }));
    

除此之外,和单机下的代码并无区别。由于客户端可能连接到集群中不同的节点,为了在集群中不同的节点之间传递消息,socket.io官方以redis的发布订阅功能为基础做了消息路由分发:socket.io-redissocket.io-redis在节点向客户端群发消息时会将该消息发布到redis的订阅队列中,让其他节点能够订阅到该消息,从而实现节点间消息推送。

  • nginx配置
worker_processes 4;
events {
  worker_connections 1024;
}
http {
  server {
    listen 80;
    location / {
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $host;
      proxy_pass http://nodes;
      # enable WebSockets
      proxy_http_version 1.1;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection "upgrade";
    }
  }
  upstream nodes {
    # enable sticky session
    ip_hash;
    server server-john:3000;
    server server-paul:3000;
    server server-george:3000;
    server server-ringo:3000;
  }
}

该配置文件,在请求头中加入 Upgrade 来支持 websocket
使用ip_hash保证同一个ip请求到固定的容器。将请求分发给四个服务:server-john,server-paul,server-george,server-ringo.

  • docker-compose配置
nginx:
  build: ./nginx
  links:
    - server-john
    - server-paul
    - server-george
    - server-ringo
  ports:
   - "3000:80"

server-john:
  build: ./server
  links:
    - redis
  expose:
    - "3000"
  environment:
    - NAME=John

server-paul:
  build: ./server
  links:
    - redis
  expose:
    - "3000"
  environment:
    - NAME=Paul

server-george:
  build: ./server
  links:
    - redis
  expose:
    - "3000"
  environment:
    - NAME=George

server-ringo:
  build: ./server
  links:
    - redis
  expose:
    - "3000"
  environment:
    - NAME=Ringo

redis:
  image: redis:alpine
  expose:
    - "6379"

该配置文件中,nginx依赖四个web服务,每个web服务都依赖redis。每个服务都暴露3000端口。environment环境变量用于标示当前的服务器名称,以此来告诉客户端请求被分发到哪台服务器了。会在index.js以下的代码中用到。

var serverName = process.env.NAME || 'Unknown';
  • 每个服务的docker配置
FROM mhart/alpine-node:6

# Create app directory
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app

# Install app dependencies
COPY package.json /usr/src/app/
RUN npm install

# Bundle app source
COPY . /usr/src/app

EXPOSE 3000
CMD [ "npm", "start" ]

该配置功能:拷贝package.json,安装依赖,拷贝源文件,启动服务。

由于示例采用docker安装,只要你的电脑中有安装docker,当我们下载完官方的代码,直接在代码根目录下运行以下命令便可启动服务进行测试。

$ docker-compose up -d
  • 注意

官网的 socket.io-redis库只有在消息广播(socket.broadcast.emit)的时候,才能生效。对于私发的消息,如果想在集群不同节点之间传递还是需要自己手动写一个解决方案,具体可以参考redis的发布订阅功能在nodejs下的api。