之前项目中有一个消息推送的模块,采用的是nodejs+socketio+redis的框架做的,打算用nginx去负载一下,结果在配置过程中发现客户端浏览器通过nginx与nodejs服务器建立的socket连接会一直中断重连,如此反复。
花了点时间才找出解决办法,特在此记录一下,也希望能给后面有同样问题的朋友一点参考:
先贴nginx.conf中的核心配置:
//配置要负载的几台机器
upstream io_nodes {
ip_hash;
server 192.168.0.101:3001 weight=1 max_fails=2 fail_timeout=30s;
server 192.168.0.101:3002 weight=1 max_fails=2 fail_timeout=30s;
server 192.168.0.101:3003 weight=1 max_fails=2 fail_timeout=30s;
}
//针对“/nodejs”的请求采用"io_nodes"负载
location /nodejs {
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;
}
以上配置测试是可用的。
其中最主要的是“ip_hash”这个配置,
配置它表示每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session的问题。
针对上面的问题,后期又有了新的发现:
推荐使用nginx的nginx-sticky-module模块替代ip_hash,采用ip_hash模式可能会超成负载不均,引用别人的话:
常见的有使用nginx自带的ip_hash来做,我想这绝对不是一个好的办法,如果前端是CDN,或者说一个局域网的客户同时访问服务器,导致出现服务器分配不均衡,以及不能保证每次访问都粘滞在同一台服务器。如果基于cookie会是一种什么情形,想想看, 每台电脑都会有不同的cookie,在保持长连接的同时还保证了服务器的压力均衡,nginx sticky值得推荐。
如果浏览器不支持cookie,那么sticky不生效,毕竟整个模块是给予cookie实现的.
/
在后面测试中,还发现有一个问题,补充一下:
打个比方,nginx后面负载了A、B、C三台nodejs服务器;
用户a与服务器A建立了socket连接,当需要向a用户推送消息时就有可能出现,请求被nginx随机分发到B或C服务器上,这时则无法将消息推送给a用户;
解决方案:使用redis的发布与订阅功能与socket.io-redis开源库,该库在节点向客户端群发消息时会将该消息发布到redis的订阅队列中,让其他节点能够订阅到该消息,从而实现节点间消息推送。
首先安装socket.io-redis;
npm install socket.io-redis
然后在nodejs 文件中加入:
var io = require(‘socket.io’)(3000);
var redis = require(‘socket.io-redis’);
io.adapter(redis({ host: ‘localhost’, port: 6379 }));
测试一下试试,OK了。
—2017-04-12 发现有如下错误一直在请求——-
WebSocket connection to
‘ws://123.123.123.123:123/socket.io/?EIO=3&transport=websocket&sid=7NvWtwxN2lvBuVyS8tRk’
failed: Error during WebSocket handshake: Unexpected response code:
400
解决方案如下:
nginx.conf里面加入:
location / {
……….
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection “Upgrade”; }
完整配置文件如下:
user root;
worker_processes 8;
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
events {
#
worker_connections 65535;
}
http {
include mime.types;
default_type application/octet-stream;
server_tokens off;
#控制服务器的hash表
server_names_hash_bucket_size 128;
#开启压缩功能
gzip on;
gzip_http_version 1.1;
gzip_vary on;
gzip_comp_level 9;
gzip_proxied any;
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/x-shockwave-flash image/png image/x-icon image/gif image/jpeg;
gzip_buffers 16 8k;
#access_log logs/access.log main;
error_log /dev/null crit;
access_log off;
#开启sendfile功能
sendfile on;
keepalive_timeout 600;
upstream io_nodes {
ip_hash;
server 111.17.117.36:3001 weight=1 max_fails=3 fail_timeout=30s;
server 111.17.117.36:3002 weight=1 max_fails=3 fail_timeout=30s;
#session_sticky;
}
server {
listen 9000;
server_name localhost;
location / {
proxy_redirect off;
proxy_buffering off;
proxy_set_header Host $host:$server_port;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
client_max_body_size 10m;
client_body_buffer_size 128k;
proxy_connect_timeout 1000;
proxy_send_timeout 1000;
proxy_read_timeout 1000;
proxy_buffer_size 256k;
proxy_buffers 128 256k;
proxy_busy_buffers_size 256k;
proxy_temp_file_write_size 256k;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_pass http://io_nodes;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}