Как заставить 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, не поможет: асинхронный рабочий может обрабатывать сразу несколько подключений, но когда два запроса приземляются на одно соединение, они обязательно будут обрабатываться один за другим.