使用 eventlet 和 Redis 进行 Flask socketio 调试会产生额外的 greenthreads?
Flask socketio debug with eventlet and Redis spawns extra greenthreads?
我正在尝试组装一个订阅 Redis 事件的简单 Flask/socketio/eventlet 服务器。我看到的行为是,在启用 Flask 调试的情况下,每次 Werkzeug 检测到更改并重新启动 socketio 时,我的另一个 redis 侦听器也会启动(旧侦听器不会退出)。
这是一个删除了所有 socketio 处理程序的工作版本:
import json
from flask import Flask, render_template
from flask_socketio import SocketIO, emit
from flask.ext.redis import FlaskRedis
import eventlet
eventlet.monkey_patch()
with open('config/flask.json') as f:
config_flask = json.load(f)
app = Flask(__name__, static_folder='public', static_url_path='')
app.config.update(
DEBUG= True,
PROPAGATE_EXCEPTIONS= True,
REDIS_URL= "redis://localhost:6379/0"
)
redis_cache = FlaskRedis(app)
socketio = SocketIO(app)
@app.route('/')
def index():
cache = {}
return render_template('index.html', **cache)
def redisReader():
print 'Starting Redis subscriber'
pubsub = redis_cache.pubsub()
pubsub.subscribe('msg')
for msg in pubsub.listen():
print '>>>>>', msg
def runSocket():
print "Starting webserver"
socketio.run(app, host='0.0.0.0')
if __name__ == '__main__':
pool = eventlet.GreenPool()
pool.spawn(redisReader)
pool.spawn(runSocket)
pool.waitall()
加入一些手动 redis-cli 发布 (PUBLISH msg himom)
这会产生以下输出:
Starting Redis subscriber
Starting webserver
* Restarting with stat
>>>>> {'pattern': None, 'type': 'subscribe', 'channel': 'msg', 'data': 1L}
Starting Redis subscriber
Starting webserver
* Debugger is active!
* Debugger pin code: 789-323-740
(22252) wsgi starting up on http://0.0.0.0:5000
>>>>> {'pattern': None, 'type': 'subscribe', 'channel': 'msg', 'data': 1L}
>>>>> {'pattern': None, 'type': 'message', 'channel': 'msg', 'data': 'himom'}
>>>>> {'pattern': None, 'type': 'message', 'channel': 'msg', 'data': 'himom'}
为什么Redis监听器会多次启动?如果我进行更改并保存它们,Werkzeug 每次都会启动另一个。我该如何正确处理这个问题?
这是所涉及的软件包及其版本的列表:
- Python 2.7.6
- 烧瓶 0.10.1
- Werkzeug 0.11.4
- 事件小程序 0.18.4
- greenlet 0.4.9
- Flask-Redis 0.1.0
- Flask-SocketIO 2.2
** 更新 **
我现在有一个部分解决方案。上面的一切都保持不变,除了池行为已移入 Flask 的 'before_first_request' 函数中:
def setupRedis():
print "setting up redis"
pool = eventlet.GreenPool()
pool.spawn(redisReader)
def runSocket():
print "Starting Webserver"
socketio.run(app, host='0.0.0.0')
if __name__ == '__main__':
app.before_first_request(setupRedis)
print app.before_first_request_funcs
runSocket()
剩下的问题是 'before_first_request' 不处理现有 websocket 的情况,但这是一个单独的问题。
添加简单 print(os.getpid())
并观察 ps aux
。您应该注意到有两个 Python 进程。更改 DEBUG=False,
并且 "problem" 没有重现,现在还有一个 Python 过程。
所以问题是您的代码创建了 redisReader
,无论它是 "worker manager" 还是实际的 "request handler" 进程。所以,不要无条件地创建和启动 pool
。请查阅 Werkzeug 文档,您的 "application init" 事件在哪里,并且只从那里开始 redisReader
。
这里的解决方案是将我的线程放入 Flask 调用的函数中:
def setupRedis():
pool = eventlet.GreenPool()
pool.spawn(redisReader)
...
app.before_first_request(setupRedis)
这解决了 Werkzeug 重新启动时留下的额外线程。
我正在尝试组装一个订阅 Redis 事件的简单 Flask/socketio/eventlet 服务器。我看到的行为是,在启用 Flask 调试的情况下,每次 Werkzeug 检测到更改并重新启动 socketio 时,我的另一个 redis 侦听器也会启动(旧侦听器不会退出)。
这是一个删除了所有 socketio 处理程序的工作版本:
import json
from flask import Flask, render_template
from flask_socketio import SocketIO, emit
from flask.ext.redis import FlaskRedis
import eventlet
eventlet.monkey_patch()
with open('config/flask.json') as f:
config_flask = json.load(f)
app = Flask(__name__, static_folder='public', static_url_path='')
app.config.update(
DEBUG= True,
PROPAGATE_EXCEPTIONS= True,
REDIS_URL= "redis://localhost:6379/0"
)
redis_cache = FlaskRedis(app)
socketio = SocketIO(app)
@app.route('/')
def index():
cache = {}
return render_template('index.html', **cache)
def redisReader():
print 'Starting Redis subscriber'
pubsub = redis_cache.pubsub()
pubsub.subscribe('msg')
for msg in pubsub.listen():
print '>>>>>', msg
def runSocket():
print "Starting webserver"
socketio.run(app, host='0.0.0.0')
if __name__ == '__main__':
pool = eventlet.GreenPool()
pool.spawn(redisReader)
pool.spawn(runSocket)
pool.waitall()
加入一些手动 redis-cli 发布 (PUBLISH msg himom) 这会产生以下输出:
Starting Redis subscriber
Starting webserver
* Restarting with stat
>>>>> {'pattern': None, 'type': 'subscribe', 'channel': 'msg', 'data': 1L}
Starting Redis subscriber
Starting webserver
* Debugger is active!
* Debugger pin code: 789-323-740
(22252) wsgi starting up on http://0.0.0.0:5000
>>>>> {'pattern': None, 'type': 'subscribe', 'channel': 'msg', 'data': 1L}
>>>>> {'pattern': None, 'type': 'message', 'channel': 'msg', 'data': 'himom'}
>>>>> {'pattern': None, 'type': 'message', 'channel': 'msg', 'data': 'himom'}
为什么Redis监听器会多次启动?如果我进行更改并保存它们,Werkzeug 每次都会启动另一个。我该如何正确处理这个问题?
这是所涉及的软件包及其版本的列表:
- Python 2.7.6
- 烧瓶 0.10.1
- Werkzeug 0.11.4
- 事件小程序 0.18.4
- greenlet 0.4.9
- Flask-Redis 0.1.0
- Flask-SocketIO 2.2
** 更新 ** 我现在有一个部分解决方案。上面的一切都保持不变,除了池行为已移入 Flask 的 'before_first_request' 函数中:
def setupRedis():
print "setting up redis"
pool = eventlet.GreenPool()
pool.spawn(redisReader)
def runSocket():
print "Starting Webserver"
socketio.run(app, host='0.0.0.0')
if __name__ == '__main__':
app.before_first_request(setupRedis)
print app.before_first_request_funcs
runSocket()
剩下的问题是 'before_first_request' 不处理现有 websocket 的情况,但这是一个单独的问题。
添加简单 print(os.getpid())
并观察 ps aux
。您应该注意到有两个 Python 进程。更改 DEBUG=False,
并且 "problem" 没有重现,现在还有一个 Python 过程。
所以问题是您的代码创建了 redisReader
,无论它是 "worker manager" 还是实际的 "request handler" 进程。所以,不要无条件地创建和启动 pool
。请查阅 Werkzeug 文档,您的 "application init" 事件在哪里,并且只从那里开始 redisReader
。
这里的解决方案是将我的线程放入 Flask 调用的函数中:
def setupRedis():
pool = eventlet.GreenPool()
pool.spawn(redisReader)
...
app.before_first_request(setupRedis)
这解决了 Werkzeug 重新启动时留下的额外线程。