tl; dr 用route
修饰的方法无法处理并发请求,而Flask是在多个工作线程和线程启动的gunicorn后面提供的,而两个不同的方法处理并发请求很好。为什么会出现这种情况,如何同时提供相同的路由呢?
我有这个简单的烧瓶应用程序:
from flask import Flask, jsonify
import time
app = Flask(__name__)
@app.route('/foo')
def foo():
time.sleep(5)
return jsonify({'success': True}), 200
@app.route('/bar')
def bar():
time.sleep(5)
return jsonify({'success': False}), 200
如果我通过以下方式运行:
gunicorn test:app -w 1 --threads 1
如果我在浏览器的两个不同标签中快速打开/bar
和/foo
,我先点击输入的标签将在5秒后加载,第二个标签将在10秒内加载。这是有道理的,因为gunicorn正在运行一个带有一个线程的worker。
如果我通过以下方式运行:
gunicorn test:app -w 1 --threads 2
gunicorn test:app -w 2 --threads 1
在这种情况下,在两个不同的标签页中打开/foo
和/bar
都需要5秒钟。这是有道理的,因为gunicorn运行的是一个有两个线程的工作者,或者两个工作者,每个都有一个线程,并且可以同时为这两条路径提供服务。
但是,如果我同时打开两个/foo
,无论枪械配置如何,第二个标签总是需要10秒。
如何获得由route
修饰的同一方法来处理并发请求?
答案 0 :(得分:9)
这个问题可能不是由Gunicorn或Flask引起的,而是由浏览器造成的。
我只是想重现它。有两个Firefox标签,它可以工作;但是如果我在不同的控制台中运行两个curl
进程,那么它们将按预期(并行)获得服务,并且它们的请求由不同的工作人员处理 - 这可以通过在启动gunicorn时启用--log-level DEBUG
来检查。 / p>
我认为这是因为Firefox(可能还有其他浏览器)为每个URL打开一个到服务器的连接;当您在两个选项卡上打开一个页面时,它们的请求将通过相同的(保持活动状态)连接发送,因此会发送给同一个工作人员。
因此,即使使用像eventlet
这样的异步工作程序也无济于事:异步工作者可以一次处理多个连接,但当两个请求落在同一个连接上时,它们将会必须一个接一个地处理。