使用 Flask 和 eventlet 响应并发请求

Responding to concurrent requests with Flask and eventlet

我尝试设置一个最小的 Flask 应用程序,它使用 eventlet 立即响应并发请求,而不是一个接一个地阻塞和响应请求(就像标准 Flask 调试网络服务器所做的那样)。

先决条件:

pip install Flask
pip install eventlet

根据我目前在互联网上找到的内容的理解,它应该是这样工作的:

# activate eventlet
import eventlet
eventlet.monkey_patch()

from flask import Flask

import datetime
from time import sleep

# create a new Flask application
app = Flask(__name__)

# a short running task that returns immediately
@app.route('/shortTask')
def short_running_task():
  start = datetime.datetime.now()
  return 'Started at {0}, returned at {1}'.format(start, datetime.datetime.now())

# a long running tasks that returns after 30s
@app.route('/longTask')
def long_running_task():
  start = datetime.datetime.now()
  sleep(30)
  return 'Started at {0}, returned at {1}'.format(start, datetime.datetime.now())

# run the webserver
if __name__ == '__main__':
    app.run(debug=True)

当运行打开此文件,然后在网络浏览器选项卡中打开 http://localhost:5000/longTask 并且它仍在处理使用 http://localhost:5000/shortTask 打开另一个选项卡时,我希望第二个选项卡 return 在第一个选项卡仍在加载时立即。但是,与在标准 Werkzeug 服务器上 运行 宁此类似,第二个选项卡仅在 returns 之后第一个选项卡在 30 秒后完成。

这里有什么问题? 顺便说一句,考虑到预计只有很少的并发用户(最多 5 个),这就是通常所说的 Flask 的 "production ready webserver" 吗?

顺便说一下,当我使用 the Flask-socketio library 到 运行 网络服务器时,根据文档,如果安装了 eventlet,它会自动选择,然后它按预期工作。

Flask-socketio 的完整示例:

# activate eventlet
import eventlet
eventlet.monkey_patch()

from flask import Flask
from flask_socketio import SocketIO

import datetime
from time import sleep

# create a new Flask application
app = Flask(__name__)

# activate Flask-socketio
socketio = SocketIO(app)

# a short running task that returns immediately
@app.route('/shortTask')
def short_running_task():
  start = datetime.datetime.now()
  return 'Started at {0}, returned at {1}'.format(start, datetime.datetime.now())

# a long running tasks that returns after 30s
@app.route('/longTask')
def long_running_task():
  start = datetime.datetime.now()
  sleep(30)
  return 'Started at {0}, returned at {1}'.format(start, datetime.datetime.now())

# run the webserver with socketio
if __name__ == '__main__':
    socketio.run(app, debug=True)
import eventlet
eventlet.monkey_patch()

不会神奇地将您的代码变成可以异步处理请求的多线程野兽(它仍然非常神奇和令人敬畏)。

正如您在 this example, you need to launch the wsgi server using eventlet wsgi's implementation 中看到的那样。

如果你想要一个标准的解决方案,看看如何使用 nginx 和 uwsgi 来启动 flask 应用程序。您也可能对利用创建完整的多线程 wsgi 处理程序的痛苦的项目 Spawning 感兴趣。

当您 运行 app.run(debug=True) 时,您明确告诉 Flask 运行 您的应用程序在基于 Werkzeug 的开发 Web 服务器上。加载eventlet没关系

如果您想 运行 您的应用程序在 eventlet web 服务器上,您必须启动一个 eventlet web 服务器,根据 the documentation 启动如下:

wsgi.server(eventlet.listen(('', 8000)), your_app)

这或多或少是 socketio.run() 在我的 Flask-SocketIO 扩展中所做的,但有一点复杂,可以选择处理 SSL。执行此操作的代码行是:https://github.com/miguelgrinberg/Flask-SocketIO/blob/539cd158f49ce085151911cb63edbacd0fa37173/flask_socketio/init.py#L391-L408。如果你环顾这些行,你会发现有三种不同的启动代码块,一种用于 werkzeug,一种用于 eventlet,一种用于 gevent。他们都是不同的。