我会尽量简短,但我有很多代码示例要展示。如果您需要更多背景信息,请告诉我!
我们最近更新了我们的 flask 服务器,以使用 flask socketio 处理 Web 套接字连接。这造成了重大的性能损失(我们使用 k6 对生产服务器进行负载测试,并在 elastic search 中为各个 api 设置了 cloudwatch 日志),这是有道理的,因为您只能为服务器使用 1 个工作器(我们之前使用了 4 个)。
为了解决这个问题,我们调整了 nginx 配置以平衡多个 socketio 服务器的负载。这很有帮助,而且整体性能肯定有所提高。但在某些页面上,页面在 15-45 秒内不会加载。这种情况发生在涉及大量 API(50+)的页面上,我猜这与 sentry 中的这个错误有关,在添加 flask socketio 之前我们从未遇到过这种情况
设置用户时出现一般异常TimeoutError: QueuePool limit of size 5 overflow 10 reached, connection timed out, timeout 30 (Background on this error at: http://sqlalche.me/e/13/3o7r)
根据此错误,似乎当同时发出过多请求并达到队列池限制时,服务器会挂起并导致加载时间极慢。因此,增加池大小和最大溢出应该有助于缓解此问题。
问题:如何优化 Flask-SocketIO 设置以消除 QueuePool 溢出错误并恢复以前的性能水平?除了增加资源限制和添加服务器节点之外,是否还应该考虑架构更改或高级技术?
这是 nginx 配置
upstream socketio_nodes {
# This needs to be enabled for web sockets to work
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 *.our-site.com;
location / {
include proxy_params;
proxy_pass http://socketio_nodes;
}
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;
}
# Consider blocking access to source maps for security reasons. These will be uploaded to sentry during build.
# location ~ \.map$ {
# deny all;
# }
}
这是服务器服务
[Unit]
Description=Gunicorn instance to serve our website
After=network.target
[Service]
User=ubuntu
Group=www-data
WorkingDirectory=/home/ubuntu/briefcase
Environment="PATH=/home/ubuntu/briefcaseEnv/bin:/usr/bin:/bin"
ExecStart=/home/ubuntu/briefcaseEnv/bin/gunicorn -k "geventwebsocket.gunicorn.workers.GeventWebSocketWorker" -w 1 --bind 0.0.0.0:%i --log-level=warning wsgi:app
在这里部署我们启动了三台服务器
sudo systemctl restart [email protected]
sudo systemctl restart [email protected]
sudo systemctl restart [email protected]
如果有帮助的话,这里是 flask socketio 相关逻辑
套接字实例
from flask_socketio import SocketIO, emit
import os
# configure cors_allowed_origins
if os.environ.get('FLASK_ENV') == 'production':
origins = [
# app url here
]
else:
# allow all origins for development
origins = "*"
# initialize your socket instance
# TODO: do we need the async_mode specified? How will this work in production?
socketio = SocketIO(async_mode='gevent', cors_allowed_origins=origins)
在 app.py 中
# monkey patch at the top of the file
from gevent import monkey
monkey.patch_all()
from libs.Sockets.socket_instance import socketio
socketio.init_app(app, message_queue='amqp://')
if parsed_args.url_map:
logging.info('\n\n########################################\n\n')
logging.info(app.url_map)
logging.info(
'\n\n########################################\n^^^ App URL Map ^^^\n')
socketio.run(app)(port=5000)
如果需要更多背景信息,请告诉我(例如 k6 摘要)