gevent-socketio + Flask + Gunicorn

Posted

技术标签:

【中文标题】gevent-socketio + Flask + Gunicorn【英文标题】: 【发布时间】:2012-11-13 02:46:03 【问题描述】:

我能否将 gevent-socketio 与 Flask 一起使用,在 Gunicorn 下运行,并且仍然享受 Flask 提供的出色的异常打印、调试器和重新加载功能?我的 gunicorn worker 和 WSGI 应用程序类会是什么样子?

【问题讨论】:

【参考方案1】:

我遇到了完全相同的问题,所以我使用看门狗解决了它。

pip install watchdog

连同这个命令:

watchmedo shell-command --patterns="*.py*;;*.less;*.css;*.js;*.txt;*.html" --recursive --command='kill -HUP `cat /tmp/gunicorn.pid` && echo "Reloading code" >> /tmp/gunicorn.log' ~/projectfolder

它需要(嗯,不是真的,但我将“重新加载代码”指向同一个日志文件,所以这是一件好事)你守护 gunicorn 进程,我这样做:

gunicorn_config.py

workers = 2
worker_class = 'socketio.sgunicorn.GeventSocketIOWorker'
bind = '0.0.0.0:5000'
pidfile = '/tmp/gunicorn.pid'
debug = True
loglevel = 'debug'
errorlog = '/tmp/gunicorn.log'
daemon = True

启动应用程序:

gunicorn run:app -c gunicorn-config.py

查看日志:

tail -f /tmp/gunicorn.log

从此时起,项目中的每次更改都应重新加载所有内容。 这有点复杂,但由于带有工作人员(或内置 socketio-server)的 gunicorn 没有任何重新加载功能,我不得不这样做。

与其他答案中的装饰器解决方案相比,这是一种不同的方法,但我喜欢保持实际代码与开发特定解决方案无关。两者都完成了同样的事情,所以我想你只需要选择你喜欢的解决方案。 :)

哦,作为额外的奖励,您可以在开发中使用生产服务器,这意味着两个环境相互匹配。

【讨论】:

不错。不过,您不需要为此对 gunicorn 进行守护进程。一个简单的“-p /tmp/gunicorn.pid”就足够了,这很好,因为您可以在控制台中看到日志,而且您可以 ^C 停止。我不得不承认,出于懒惰的原因,我起初更喜欢另一个答案。然而,在 gunicorn 中重新加载需要 SIGHUP,这似乎是一种足够体面的方式。不过,我不喜欢在另一个窗口中运行 watchmedo,实际上我正在考虑在我的 app.py 中放置一个 watchdog.observer(在 __name__ == '__main__' 下,仅用于开发) 好吧...将看门狗添加到我的特定项目将不起作用——我正在使用 gevent。我想我必须在不同的过程中这样做。 Nitzan,我一直在玩创建一个看门狗进程和 gunicorn 进程,但最后我对奇怪的结果感到非常恼火,我只使用了两个窗口。我喜欢 daemonized 设置,因为你不能不小心将其关闭,然后拖尾日志文件与观看 stdout 的结果相同 :) 是的,但你不能 ^c 它...我还试图通过编写一个会发送 sighup 的看门狗“技巧”来概括你的想法,我在意识到 gevent 事情后放弃了. 但是你可以使用 kill -9 /tmp/gunicorn.pid 来关闭它。所有命令的简单别名,并没有那么痛苦。我很确定当我厌倦了这个烂摊子时,我会改善它。 ^^【参考方案2】:

我最近一直在研究这个主题。我不认为你可以轻松地使用 Flask + gevent-socket.io + Gunicorn 的自动重载功能。 Gunicorn 是一个生产服务器,本身不允许此类功能。

但是,我为我的开发服务器找到了一个不错的解决方案:用户 Socketioserver 提供了该库和一个用于自动重载的 Flask sn-p。这是启动脚本(runserver.py):

from myapp import app
from gevent import monkey
from socketio.server import SocketIOServer
import werkzeug.serving

# necessary for autoreload (at least)
monkey.patch_all()

PORT = 5000

@werkzeug.serving.run_with_reloader
def runServer():
    print 'Listening on %s...' % PORT
    ws = SocketIOServer(('0.0.0.0', PORT), app, resource="socket.io", policy_server=False)
    ws.serve_forever()

runServer()

此解决方案的灵感来自:http://flask.pocoo.org/snippets/34/

【讨论】:

我刚刚注意到我用这种方法丢失了漂亮的 werkzeug 调试页面......正在调查它。 已编辑答案以包含调试页面。使用 werkzeug.debug.DebuggedApplication :werkzeug.pocoo.org/docs/debug 坏消息:DebuggedApplication 中断了 websockets 服务 :-( 删除它。 谢谢。我喜欢 DebuggedApplication,并为此 +1(不关心这个特定项目的 WebSockets)。然而,重新加载似乎并不顺利——它将重新加载并重新启动 Gunicorn,这将创建更多进程,将旧进程留在原处,因此不允许我重新绑定端口。查看 gunicorn 代码后,似乎需要重新启动它时发生了很多事情,正确的方法是使用 SIGHUP。【参考方案3】:

我对 Werkzeug 调试器进行了一些调整,现在它可以与 socket.io 命名空间一起使用,请参阅下文并享受 :)

https://github.com/aldanor/SocketIO-Flask-Debug

【讨论】:

以上是关于gevent-socketio + Flask + Gunicorn的主要内容,如果未能解决你的问题,请参考以下文章

gevent-socketio 从线程发送消息

python.h 在尝试安装 gevent-socketio 时不喜欢

使用 gevent-socketio 和 Socket.IO.js 的 Python 瓶子微框架的最小示例

使用 Python3 在 Pyramid 中使用 Websocket

使用一个服务器进程从 Django 应用程序推送通知

Flask第八章:Flask之flask实例化配置以及flask对象配置