Как заставить Flask/Gunicorn обрабатывать параллельные запросы для одного и того же маршрута?
tl; dr Метод, декорированный с помощью route
, не может обрабатывать параллельные запросы, в то время как Flask обслуживается за ружьем, запущенным с несколькими рабочими и потоками, в то время как два разных метода отлично обрабатывают параллельные запросы. Почему это так, и как можно одновременно обслуживать один и тот же маршрут?
У меня есть это простое флеш-приложение:
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 test:app -w 1 --threads 2
gunicorn test:app -w 2 --threads 1
В этом случае открытие /foo
и /bar
на двух разных вкладках занимает 5 секунд. Это имеет смысл, потому что у пушки есть либо один рабочий с двумя нитями, либо два сотрудника с одним потоком каждый и могут одновременно обслуживать оба маршрута.
Однако, если я открываю одновременно два /foo
, независимо от конфигурации пушки, вторая вкладка всегда будет занимать 10 секунд.
Как я могу получить тот же метод, украшенный route
для обслуживания одновременных запросов?
Ответы
Ответ 1
Эта проблема, вероятно, не вызвана Gunicorn или Flask, а браузером.
Я просто попытался воспроизвести его. С двумя вкладками Firefox он работает; но если я запускаю два процесса curl
в разных консолях, тогда они обслуживаются как ожидалось (параллельно), а их запросы обрабатываются разными работниками - это можно проверить, включив --log-level DEBUG
при запуске пушки.
Я думаю, что это потому, что Firefox (и, возможно, другие браузеры) открывают одно соединение с сервером для каждого URL-адреса; и когда вы открываете одну страницу на двух вкладках, их запросы отправляются через одно и то же (поддерживаемое) соединение и в результате попадают к одному и тому же работнику.
В результате даже использование async-работника, такого как eventlet
, не поможет: асинхронный рабочий может обрабатывать сразу несколько подключений, но когда два запроса приземляются на одно соединение, они обязательно будут обрабатываться один за другим.