最近在使用flask_socketio,项目写好之后, 本来准备像平常一样使用gunicorn直接开多个worker开开心心上线。结果发觉各种报错,websocket链接失败。差不多就是下面这种报错刷屏的感觉。
无奈只能继续去看文档,对于一个编程菜鸡+英文渣+文档阅读障碍者,花了半天终于大概弄明白作者给出的多workers的实现方法了。为了方便和我一样的鶸,在这里分享一下。
按文档所说的要实现多worker必须满足以下两个条件:
首先,由于使用了websocket,就需要保证保证从一个客户端来的请求必须总是由同一个worker处理,这也被称为粘性会话(sticky sessions),在nginx中可以用ip_hash来实现。然而gunicorn本身负载均衡算法不支持粘性会话,所以不能用gunicorn,作者推荐使用uwsgi。
其次,由于每个服务器实际上只和客户端中的一部分连接,所以要使用Redis或RabbitMQ作为服务器的消息队列来协同处理复杂的操作,例如广播,广播房间(rooms)
上面的看不懂?没关系,反正我也不怎么懂,下面直接上相关代码,可以快乐的复制了:
作者给出的实现思路如下图:
1、使用消息队列:
由于我使用的是redis所以python 中的代码是下面的形式,具体路径写法参照文档。一定要写对,可以先把url写出来用r.info()之类的方法确认下连接,当初生产环境的redis地址没写对,报的错误是ws连接错误,范围大的不知道到哪去定位错误,血的教训:
socketio=SocketIO(app,message_queue='redis://')
2、nginx配置
这里是使用了8000,8001,80002,8003四个端口来作为server的端口,不懂的同学基本可以照抄,根据需求改一下端口号和日志路径即可,强力党自便。
upstream socketio_nodes {
ip_hash;
server 127.0.0.1:8000;
server 127.0.0.1:8001;
server 127.0.0.1:8002;
server 127.0.0.1:8003;
# to scale the app, just add more nodes here!
}
server {
listen 80;
server_name dispatch-sandbox.iqusong.com;
access_log /var/log/nginx/access_log main;
error_log /var/log/nginx/error_log info;
location / {
proxy_pass http://socketio_nodes;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /socket.io{
proxy_pass http://socketio_nodes/socket.io;
proxy_http_version 1.1;
proxy_redirect off;
proxy_buffering off;
proxy_set_header Host $host;
proxy_set_header X-Real-UP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
}
}
3、使用uwsgi启动
首先安装uwsgi,可以使用pip,不会的自行百度/google
然后打开4个终端分别输入下面命令,启动4个uwsgi服务,其中run_server.py替换成自己的flask启动文件的名字
uwsgi --http :8000 --gevent 1000 --http-websockets --master --wsgi-file run_server.py --callable app
uwsgi --http :8001 --gevent 1000 --http-websockets --master --wsgi-file run_server.py --callable app
uwsgi --http :8002 --gevent 1000 --http-websockets --master --wsgi-file run_server.py --callable app
uwsgi --http :8003 --gevent 1000 --http-websockets --master --wsgi-file run_server.py --callable app
然后就可以使用你的websocket服务了,可以看见相同的ip访问的都是同一个端口,关掉当前使用端口的服务,会自动连接到另外一个端口。看起来是成功了。
todo:uwsgi没用过,但是开启4个终端输入命令的方式实在是太蠢了,找找有什么优雅的同时启动4个uwsgi的方式