问题: 最近使用flask开发的websocket服务,要开始上线了,本地开发环境测试一切的OK.使用Gunicorn部署后,如果在/etc/resolv.conf
中有配置 一个不能解析的域名就会出现,所有请求的接口都需要30秒后才能有响应,看起来好像被阻塞了. 如果在/etc/resolve.conf
中没有配置相关的nameserver或者search 相关的记录.是没有问题的,服务器难免可能会配置到这个文件,因为这个文件是系统的文件.在项目中我们是不可控制. 启用了Gunicorn的调试模式查看日志,有一些错误信息.但是接口是可以也能正常响应,只是响应时间比较长.因为这样是web系统就根本不能正常使用,用户体验不好.
故障排出:
日志中并没有发现有用的信息.
反复修改nginx代理的相关配置,问题并没有改善.
由于使用1的方法一样的没有解决,因此绕过nginx直接访问接口没问题依旧.
参考文章:Flask-SocketIO实践Demo
按照官方的相关配置,问题依旧,这个时候根据经验,感觉可能是项目中的依赖兼容性问题.正好我查看了自己以前写过的一篇关于flask-socketio的博客.在核对了一下自己生产环境中相关依赖的版本都对不上.尤其是Gunicorn的版本是最新版本.因此我按照以前记录的版本更新了flask-soketio的版本,我在在网上搜索了一下Gunicorn和flask-socketio的版本兼容性问题,有人写了这么一句话:Gunicorn 18.0版本是被推荐和Flask-SocketIO搭配的版本。19.x版本已知在带有WebSocket的一些特定部署场景下存在不兼容的情况。我试着按照上面的版本对部署的环境中的Gunicorn的版本进行了降级到Gunicorn 18.0.这样再次启动服务,发现所有的接口响应都恢复了正常,这个时候我在修改/etc/resolv.conf里面的记录信息,添加了很多根本不存在的记录.再次重启服务.接口依然好用.
至此,完美的解决这个问题.
import eventlet
eventlet.monkey_patch()
from api import app
from api.events import socketio
from config import ReadConfiguration
if __name__ == '__main__':
project_config = ReadConfiguration()
host, port, debug = project_config.start_config()
socketio.run(app, host=host, port=int(port))
启动文件gunicorn.py
# 端口 5000
bind = '0.0.0.0:9090'
# 设置守护进程,将进程交给supervisor管理
daemon = 'false'
# 工作模式协程
worker_class = 'eventlet'
# 设置最大并发量
worker_connections = 2000
# 设置进程文件目录
pidfile = '/var/run/gunicorn.pid'
# 设置访问日志和错误信息日志路径
#accesslog = '/var/error.log'
# errorlog = '/var/error.log'
accesslog = '-' # 记录到标准输出
# 设置日志记录水平
loglevel = 'error'
gunicorn -c gunicorn.py run:app
使用nginx作为前端的反向代理将请求传递给应用是可行的。然而,只有nginx 1.4版本以上才支持WebSocket协议。下面是nginx代理HTTP和WebSocket请求的一个最基本的配置:
upstream socketio_nodes {
ip_hash;
server 127.0.0.1:5000;
server 127.0.0.1:5001;
server 127.0.0.1:5002;
# to scale the app, just add more nodes here!
}
server {
listen 80;
server_name _;
location / {
include proxy_params;
proxy_pass http://127.0.0.1:5000;
}
location /socket.io {
include proxy_params;
proxy_http_version 1.1;
proxy_buffering off;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_pass http://socketio_nodes/socket.io;
}
}
老版本的Flask-SocketIO有完全不同的一系列依赖包。老版本依赖gevent-socketio和gevent-websocket,这些包 1.0 版本都不需要了。
尽管依赖的改变,但是 1.0 版本却没有太多重要的改变。下面是一个实际改变的详细的清单:
1.0 版本放弃支持Python 2.6,增加了对Python 3.3, Python 3.4 和 pypy 的支持。
0.x 版本需要老版本的Socket.IO javascript客户端。从 1.0 版本开始,支持新发布的Socket.IO和Engin.IO。1.0版本以前的Socket.IO将不再被支持。Swift和C++官方的Socket.IO客户端也被支持。
0.x 版本依赖gevent,gevent-socketio和gevent-websocket.1.0 版本以后将不再使用。在Flask开发的网络服务器中,gevent是三种后端网络服务器选择之一,另外两个是eventlet和其它常规多线程WSGI服务器。
Socket.IO服务器选项在 1.0 版本中也有所改变。它们可以由SocketIO构造函数来提供,或者由run()调用。这些选项在使用前在这两者中被合并。
0.x 版本暴露了gevent-socketio在连接中作为request.namespace。在 1.0 版本中它不再被使用。这个请求对象定义了request.namespace作为待处理的命令空间。并且增加了request.aid,为客户端连接定义了一个独有的会话ID,request.event包含了活动名称和参数。
为了获得房间列表,0.x版本需要应用使用私有gevent-socketio结构,包含request.namespace.rooms表达式。这是在 1.0 版本中将不再出现,因为它包含了一个合适的room()函数。
这个推荐的“把戏(trick)”发送消息到一个独立的客户端将消息分发到每个客户端所在的独立的房间内,这个地址消息对应着目的房间(desired room)。这个特性在 1.0 版本中被正式化了,当客户端连接到服务器时,它会立即自动地被分配到一个特定的房间内。
全局命名空间的connect活动在 1.0 版本之前并没有被触发。这bug已经被修复了并且按照预期触发。
在 1.0 版本增加了对客户端的回调函数的支持。
为了升级到新的Flask-SocketIO版本,你需要升级你的Socket.IO客户端到兼容Socket.IO 1.0 协议。对于Javascript客户端,1.3.x和1.4.x版本经过充分地测试,发现是兼容的。
在服务端,有一些要点是要被考虑到的:
如果你想继续使用gevent,那么gevent-socketio需要从你的虚拟环境中卸载,因为这个包将不再需要并且可能会与它的替代——python-socketio相冲突。
如果你想轻微地提高性能和稳定性,那么推荐你转而使用eventlet。为了做到这一点,需要卸载gevent、gevent-socketio和gevent-websocket,然后安装eventlet。
如果你的应用使用了猴子修复了并转向了eventlet,需要调用eventlet.monkey_patch()来代替gevent中的monkey.patch_all()。此外,任何对gevent的调用必须被同等条件下的对eventlet调用替代。
任何使用request.namespace需要被直接调用Flask-SocketIO函数替代。例如,request.namespace.rooms要用rooms()函数替换。
任何使用内置的gevent-socketio的对象都必须被去除,当这个包不再是所需的依赖的时候。