Flask-SocketIO + uWSGI。与 Mule worker 一起发射
Flask-SocketIO + uWSGI. Emit with Mule worker
最近我在我的 Flask 应用程序中实现了 WebSocket 支持。
设置如下:
1. Python 2.7
2. Flask + Flask-SocketIo
3. SocketIO(客户端)
4. NGINX 作为代理
5. uWSGI 2.0.13 + 它是带有 Gevent 的 WebSocket(猴子补丁)
我已经设法启用套接字连接,但我在 UWSGI Worker 之间的发射同步上遇到困难,尤其是单个 mule。
我有专门的 UWSGI mule 来监控一些东西并每 30 秒报告一次状态,将其广播给连接的客户端。如果我允许我的任务 运行 在任何可用的工作人员(不仅是 mule 专用)上发出仅到达以前通过完全相同的过程作为服务器的客户端,例如:
客户端请求由 Worker #6 提供(400 PID)并建立套接字连接。
后来,一个 mule 任务由完全相同的 Worker 提供,因此客户端收到了 Emit)如果任何其他 worker 处理了该任务,则 emit 不会被客户端缓存。
在单一工作者模式下一切正常,但这显然不是可接受的解决方案。
以下是一些技术细节:
uwsgi.ini
[uwsgi]
#application's base folder
base = /home/ubuntu/application_test
#python module to import
app = manage
module = %(app)
home = %(base)/venv
virtualenv = %(base)/venv
pythonpath = %(base)
#socket file's location
socket = %(base)/application_test.sock
#permissions for the socket file
chmod-socket = 666
#the variable that holds a flask application inside the module imported at
line #6
callable = app
#location of log files
logto = /var/log/uwsgi/%n.log
processes = 10
#WebSocket
http-websockets = true
gevent = 1000
enable-threads = true
die-on-therm = true
vacuum = true
mule=%(base)/application_test/uwsgi_mules/metrics_mule.py
Flask 应用程序
(...)
from flask_socketio import SocketIO
app = Flask(__name__)
socketio = SocketIO(app,message_queue='redis://')
(...)
工人发出代码
socket = SocketIO(message_queue='redis://')
def broad_cast_server_info(state, health):
socket.emit('sys_state', health)
socket.emit('system_snapshot', state)
客户端
socket.on('sys_state', function(data){
(...)
}
});
如果我使用带有确认响应的客户端socket.emit,一切正常。
当我在 mule 中简单地导入 socketio 并在其上调用 emit 时,我遇到了上述问题。但是,如果我遵循:
https://flask-socketio.readthedocs.io/en/latest/#emitting-from-an-external-process
每次 mule 运行工作时我都会收到一个 Redis 错误。
File "/home/ubuntu/application_test/venv/local/lib/python2.7/site-packages/flask_socketio/init.py", line 365, in emit
skip_sid=skip_sid, callback=callback, **kwargs)
File "/home/ubuntu/application_test/venv/local/lib/python2.7/site-packages/socketio/server.py", line 228, in emit
**kwargs)
File "/home/ubuntu/application_test/venv/local/lib/python2.7/site-packages/socketio/pubsub_manager.py", line 66, in emit
'skip_sid': skip_sid, 'callback': callback})
File "/home/ubuntu/application_test/venv/local/lib/python2.7/site-packages/socketio/redis_manager.py", line 62, in _publish
return self.redis.publish(self.channel, pickle.dumps(data))
File "/home/ubuntu/application_test/venv/local/lib/python2.7/site-packages/redis/client.py", line 2034, in publish
return self.execute_command('PUBLISH', channel, message)
File "/home/ubuntu/application_test/venv/local/lib/python2.7/site-packages/redis/client.py", line 673, in execute_command
connection.send_command(*args)
File "/home/ubuntu/application_test/venv/local/lib/python2.7/site-packages/redis/connection.py", line 610, in send_command
self.send_packed_command(self.pack_command(*args))
File "/home/ubuntu/application_test/venv/local/lib/python2.7/site-packages/redis/connection.py", line 585, in send_packed_command
self.connect()
File "/home/ubuntu/application_test/venv/local/lib/python2.7/site-packages/redis/connection.py", line 489, in connect
raise ConnectionError(self._error_message(e))
redis.exceptions.ConnectionError: Error -2 connecting to redispass:6379. Name or service not known.
画中画冻结:
alabaster==0.7.9
amqp==2.1.1
aniso8601==1.2.0
appdirs==1.4.3
astroid==1.4.8
Babel==2.3.4
beautifulsoup4==4.5.1
certifi==2017.11.5
chardet==3.0.4
click==6.7
decorator==4.0.11
defusedxml==0.5.0
dict2xml==1.5
dicttoxml==1.7.4
docopt==0.6.2
enum-compat==0.0.2
enum34==1.1.6
eventlet==0.21.0
Flask==0.12
Flask-Login==0.4.0
Flask-RESTful==0.3.5
flask-restful-swagger-2==0.33
Flask-Script==2.0.5
Flask-SocketIO==2.9.2
flask-swagger-ui==0.0.3
Flask-WTF==0.14.2
gevent==1.2.2
greenlet==0.4.12
html5lib==1.0b8
hurry.filesize==0.9
hypchat==0.21
idna==2.6
infinity==1.4
intervals==0.8.0
itsdangerous==0.24
Jinja2==2.9.5
jira==1.0.10
lazy-object-proxy==1.2.2
lxml==3.8.0
MarkupSafe==1.0
monotonic==1.4
oauthlib==2.0.2
olefile==0.44
ordereddict==1.1
packaging==16.8
pbr==3.0.1
pdfkit==0.6.1
Pillow==4.0.0
pql==0.4.3
psutil==5.4.1
py==1.4.34
pymongo==3.4.0
pyodbc==4.0.15
pyparsing==2.2.0
PyPDF2==1.26.0
pypyodbc==1.3.4
python-dateutil==2.6.0
python-engineio==2.0.1
python-memcached==1.58
python-socketio==1.8.3
pytz==2016.10
reportlab==3.4.0
requests==2.13.0
requests-oauthlib==0.8.0
requests-toolbelt==0.8.0
six==1.10.0
SQLAlchemy==1.1.6
SQLAlchemy-Utils==0.32.14
suds==0.4
urllib3==1.22
validators==0.11.3
vine==1.1.3
webencodings==0.5.1
Werkzeug==0.12
wrapt==1.10.10
WTForms==2.1
xmltodict==0.10.2
WTForms-Components==0.10.3
我对以下情况很生气:
-猴子补丁Gevent
- 通过 uwsgi 的早期猴子补丁 Gevent(假设在 uWSGI 2.1 上工作)
- 添加 Redis 队列
是否有任何其他方法可以使 Flask-SocketIO 与 uWSGI worker 同步工作?
好像是redis的问题
你安装并启动了吗?
运行
redis-cli -h localhost -p 6379
确保 flask-socketIO 可以连接到它
@shalbafzadeh 给了我一个惊人的线索。答案如下:
uWSGI 多工作模式中的 Flask-SocketIO 需要 monkey-patchin 和文档中描述的一些消息队列机制,比如 Redis。使 Redis 正常工作,解决了所描述的问题。 Redis部分来了:
&TL;DN
Redis 连接字符串无效。 Redis 不支持 url 中的特殊字符,即使是编码的。
Flask-SocketIO 需要连接字符串到 redis(代码隐藏使用 redis.Redis.from_url() 方法)。我在发布问题时犯了一个明显的错误,跳过了我的 redis 受密码保护的事实,所以 url 实际上看起来如下:
'redis://[:RedIsPass#]@localhost:6379/1'
然而,由于密码字符串的规范化并将其加载为主机,错误消息有点误导。它删除特殊字符并将所有内容小写。这使我收到错误消息,例如:
Error -2 connecting to redispass:6379. Name or service not known.
当我将连接字符串固定为:
'redis://:RedIsPass#@localhost:6379/1'
我遇到了另一个问题,这次是 "Invalid IPV6" 错误。
这是由密码中的#引起的。遗憾的是,Redis 库没有 URL 解码,因此即使将 # 替换为 %23 也无法解决问题,因为 %23 作为纯文本传递并且与密码不匹配。必须更改服务器上的密码。
普通密码就可以了。
最近我在我的 Flask 应用程序中实现了 WebSocket 支持。
设置如下: 1. Python 2.7 2. Flask + Flask-SocketIo 3. SocketIO(客户端) 4. NGINX 作为代理 5. uWSGI 2.0.13 + 它是带有 Gevent 的 WebSocket(猴子补丁)
我已经设法启用套接字连接,但我在 UWSGI Worker 之间的发射同步上遇到困难,尤其是单个 mule。
我有专门的 UWSGI mule 来监控一些东西并每 30 秒报告一次状态,将其广播给连接的客户端。如果我允许我的任务 运行 在任何可用的工作人员(不仅是 mule 专用)上发出仅到达以前通过完全相同的过程作为服务器的客户端,例如: 客户端请求由 Worker #6 提供(400 PID)并建立套接字连接。 后来,一个 mule 任务由完全相同的 Worker 提供,因此客户端收到了 Emit)如果任何其他 worker 处理了该任务,则 emit 不会被客户端缓存。
在单一工作者模式下一切正常,但这显然不是可接受的解决方案。
以下是一些技术细节: uwsgi.ini
[uwsgi]
#application's base folder
base = /home/ubuntu/application_test
#python module to import
app = manage
module = %(app)
home = %(base)/venv
virtualenv = %(base)/venv
pythonpath = %(base)
#socket file's location
socket = %(base)/application_test.sock
#permissions for the socket file
chmod-socket = 666
#the variable that holds a flask application inside the module imported at
line #6
callable = app
#location of log files
logto = /var/log/uwsgi/%n.log
processes = 10
#WebSocket
http-websockets = true
gevent = 1000
enable-threads = true
die-on-therm = true
vacuum = true
mule=%(base)/application_test/uwsgi_mules/metrics_mule.py
Flask 应用程序
(...)
from flask_socketio import SocketIO
app = Flask(__name__)
socketio = SocketIO(app,message_queue='redis://')
(...)
工人发出代码
socket = SocketIO(message_queue='redis://')
def broad_cast_server_info(state, health):
socket.emit('sys_state', health)
socket.emit('system_snapshot', state)
客户端
socket.on('sys_state', function(data){
(...)
}
});
如果我使用带有确认响应的客户端socket.emit,一切正常。
当我在 mule 中简单地导入 socketio 并在其上调用 emit 时,我遇到了上述问题。但是,如果我遵循: https://flask-socketio.readthedocs.io/en/latest/#emitting-from-an-external-process
每次 mule 运行工作时我都会收到一个 Redis 错误。
File "/home/ubuntu/application_test/venv/local/lib/python2.7/site-packages/flask_socketio/init.py", line 365, in emit skip_sid=skip_sid, callback=callback, **kwargs) File "/home/ubuntu/application_test/venv/local/lib/python2.7/site-packages/socketio/server.py", line 228, in emit **kwargs) File "/home/ubuntu/application_test/venv/local/lib/python2.7/site-packages/socketio/pubsub_manager.py", line 66, in emit 'skip_sid': skip_sid, 'callback': callback}) File "/home/ubuntu/application_test/venv/local/lib/python2.7/site-packages/socketio/redis_manager.py", line 62, in _publish return self.redis.publish(self.channel, pickle.dumps(data)) File "/home/ubuntu/application_test/venv/local/lib/python2.7/site-packages/redis/client.py", line 2034, in publish return self.execute_command('PUBLISH', channel, message) File "/home/ubuntu/application_test/venv/local/lib/python2.7/site-packages/redis/client.py", line 673, in execute_command connection.send_command(*args) File "/home/ubuntu/application_test/venv/local/lib/python2.7/site-packages/redis/connection.py", line 610, in send_command self.send_packed_command(self.pack_command(*args)) File "/home/ubuntu/application_test/venv/local/lib/python2.7/site-packages/redis/connection.py", line 585, in send_packed_command self.connect() File "/home/ubuntu/application_test/venv/local/lib/python2.7/site-packages/redis/connection.py", line 489, in connect raise ConnectionError(self._error_message(e)) redis.exceptions.ConnectionError: Error -2 connecting to redispass:6379. Name or service not known.
画中画冻结:
alabaster==0.7.9
amqp==2.1.1
aniso8601==1.2.0
appdirs==1.4.3
astroid==1.4.8
Babel==2.3.4
beautifulsoup4==4.5.1
certifi==2017.11.5
chardet==3.0.4
click==6.7
decorator==4.0.11
defusedxml==0.5.0
dict2xml==1.5
dicttoxml==1.7.4
docopt==0.6.2
enum-compat==0.0.2
enum34==1.1.6
eventlet==0.21.0
Flask==0.12
Flask-Login==0.4.0
Flask-RESTful==0.3.5
flask-restful-swagger-2==0.33
Flask-Script==2.0.5
Flask-SocketIO==2.9.2
flask-swagger-ui==0.0.3
Flask-WTF==0.14.2
gevent==1.2.2
greenlet==0.4.12
html5lib==1.0b8
hurry.filesize==0.9
hypchat==0.21
idna==2.6
infinity==1.4
intervals==0.8.0
itsdangerous==0.24
Jinja2==2.9.5
jira==1.0.10
lazy-object-proxy==1.2.2
lxml==3.8.0
MarkupSafe==1.0
monotonic==1.4
oauthlib==2.0.2
olefile==0.44
ordereddict==1.1
packaging==16.8
pbr==3.0.1
pdfkit==0.6.1
Pillow==4.0.0
pql==0.4.3
psutil==5.4.1
py==1.4.34
pymongo==3.4.0
pyodbc==4.0.15
pyparsing==2.2.0
PyPDF2==1.26.0
pypyodbc==1.3.4
python-dateutil==2.6.0
python-engineio==2.0.1
python-memcached==1.58
python-socketio==1.8.3
pytz==2016.10
reportlab==3.4.0
requests==2.13.0
requests-oauthlib==0.8.0
requests-toolbelt==0.8.0
six==1.10.0
SQLAlchemy==1.1.6
SQLAlchemy-Utils==0.32.14
suds==0.4
urllib3==1.22
validators==0.11.3
vine==1.1.3
webencodings==0.5.1
Werkzeug==0.12
wrapt==1.10.10
WTForms==2.1
xmltodict==0.10.2
WTForms-Components==0.10.3
我对以下情况很生气: -猴子补丁Gevent - 通过 uwsgi 的早期猴子补丁 Gevent(假设在 uWSGI 2.1 上工作) - 添加 Redis 队列
是否有任何其他方法可以使 Flask-SocketIO 与 uWSGI worker 同步工作?
好像是redis的问题
你安装并启动了吗?
运行
redis-cli -h localhost -p 6379
确保 flask-socketIO 可以连接到它
@shalbafzadeh 给了我一个惊人的线索。答案如下:
uWSGI 多工作模式中的 Flask-SocketIO 需要 monkey-patchin 和文档中描述的一些消息队列机制,比如 Redis。使 Redis 正常工作,解决了所描述的问题。 Redis部分来了:
&TL;DN Redis 连接字符串无效。 Redis 不支持 url 中的特殊字符,即使是编码的。
Flask-SocketIO 需要连接字符串到 redis(代码隐藏使用 redis.Redis.from_url() 方法)。我在发布问题时犯了一个明显的错误,跳过了我的 redis 受密码保护的事实,所以 url 实际上看起来如下:
'redis://[:RedIsPass#]@localhost:6379/1'
然而,由于密码字符串的规范化并将其加载为主机,错误消息有点误导。它删除特殊字符并将所有内容小写。这使我收到错误消息,例如:
Error -2 connecting to redispass:6379. Name or service not known.
当我将连接字符串固定为:
'redis://:RedIsPass#@localhost:6379/1'
我遇到了另一个问题,这次是 "Invalid IPV6" 错误。 这是由密码中的#引起的。遗憾的是,Redis 库没有 URL 解码,因此即使将 # 替换为 %23 也无法解决问题,因为 %23 作为纯文本传递并且与密码不匹配。必须更改服务器上的密码。 普通密码就可以了。