socket.io是目前较为流行的web实时推送框架,其基于nodejs语言开发,底层用engine.io实现。 借助nodejs语言异步的特性,其得到了不错的性能。但单个实例的socket.io依然承载能力有限,最多只能容纳3000个long-polling方式的客户端进行链接。node
将socket.io进行分布式扩展的难点有两处:nginx
1. 进行负载均衡时客户端必须保证始终连到一个节点上git
若是客户端采用long-polling长轮训方式进行链接,则每次轮训都会产生一个新的请求,若不进行限制。就有可能链接到集群内新的 socket.io节点上,致使异常的发生。github
解决方法:使用nginx的ip_hash实现session sticky ,让客户端始终链接到集群内一台节点上。web
2. 多个实例之间的消息推送redis
当集群内某台节点想要向链接到集群的全部客户端发送消息时,某些客户端由于负载均衡时ip_hash可能被分配到了其余的节点上,这时就须要向其余节点发布推送消息,让其余节点的同时向客户端进行推送。mongodb
解决方法:使用redis的发布与订阅功能与socket.io-redis开源库,该库在节点向客户端群发消息时会将该消息发布到redis的订阅队列中,让其余节点可以订阅到该消息,从而实现节点间消息推送。shell
上图是采用该架构的一个聊天服务器集群示例,每一个chatnode至关于一个socket.io实例,其中的chatModule负责客户端链接,adminModule负责聊天服务器的管理功能。npm
adminnode做为整个集群的管理节点,经过redis的消息订阅功能来与各个chatnode通讯, 并经过开放http接口来与外部系统进行交互。服务器
准备安装的软件:
nginx, nodejs, redis以及一个socket.io应用,如一个聊天服务器,例子请见官网这里。
具体步骤:
1.将socket.io应用部署成两个实例,如在同一台主机上为每一个实例分配不一样的端口号4000, 5000:
http.listen(4000, function(){ console.log('listening on *:4000'); });
2.配置nginx文件,设置负载均衡proxy
upstream chat_nodes { ip_hash; server 127.0.0.1:4000; server 127.0.0.1:5000; }
以及反向代理设置 (注意为了支持websocket协议,需将nginx升级至1.3.12版本以上
location / { proxy_pass http://chat_nodes; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; }
完成配置后,重启nginx。
3.安装nodejs模块 socket.io-redis
sudo npm install socket.io-redis
4.在原来socket.io应用中初始化io的位置加入io的redis适配器:
var redis = require('socket.io-redis'); io.adapter(redis({ host: 'localhost', port: 6379 }));
5. 重启各个socket.io应用,进行测试。
其余注意点:
因为nginx的反向代理机制和socket.io的自动重连机制,上述架构还具有高可用的特性,即当某个节点down机时,原先链接到该节点上的客户端会自动重连至其它节点上。
节点的数量能够随时增减,不须要暂停服务,只需修改nginx配置便可。
nginx的ip_hash是基于ip的前三段进行计算的,也就是说ip只有D段不一样的两台客户端必定会链接到同一台服务器上,这点测试的时候须要注意。
能够经过redis的订阅发布服务来实现其余系统同集群的通讯,完成集群的管理工做。
因为是分布式环境,因此节点内存中存储的信息(如用户、房间信息)能够考虑持久化到redis或mongodb中。