Flask о масштабируемости Gunicorn/ Cheroot
Я разработал конечную точку HTTP API, используя флягу, которая принимает данные json по запросам POST и отправляет ответ json.
Я пробовал использовать несколько серверов WSGI: gunicorn, cheroot, Bjoern за Nginx в качестве обратного прокси.
Я заметил, что независимо от того, какой сервер WSGI я использую, приложение не может обрабатывать постоянную нагрузку в 500 запросов в секунду. Внезапный всплеск 500 - это нормально. Но не тогда, когда он поддерживается. Запросы начинают получать отложенные ответы, а довольно много запросов просто истекает.
Сеть Flask развернута на 24-ядерном физическом сервере. Итак, у него 48 логических ядер. Я использую приложение C ++ на другом аналогичном 24-ядерном сервере, чтобы запускать эти запросы асинхронно. Один запрос каждые 2 мс, то есть 500 в секунду.
Рассмотрим приведенный ниже пример простого однофайлового приложения flask на сервере Cheroot WSGI, которое я создал для тестирования производительности. Он только регистрирует запрос json и отправляет ответ json. Даже это не способно справиться с устойчивой нагрузкой в 500 запросов на мощный 24-ядерный физический сервер. Во время теста загрузка ЦП всегда ниже 5%.
import os
import json
import logging
from logging.handlers import TimedRotatingFileHandler
from flask import Flask
from flask import request, jsonify
from cheroot.wsgi import PathInfoDispatcher
from cheroot.wsgi import Server
app = Flask(__name__)
# Setup logger for the app
if not os.path.exists('logs'):
os.mkdir('logs')
file_handler = TimedRotatingFileHandler('logs/simpleflaskapp.log', when='midnight', interval=1, backupCount=10)
file_handler.setFormatter(logging.Formatter(
'%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]'))
file_handler.setLevel(logging.INFO)
app.logger.addHandler(file_handler)
app.logger.setLevel(logging.INFO)
app.logger.info("simpleflaskapp startup")
# end setup logger
@app.route( '/test', methods = [ 'POST' ] )
def test():
app.logger.info(json.dumps(request.json))
res = {
"statusCode": 200,
"message": "OK",
}
return jsonify(res)
d = PathInfoDispatcher({'/': app})
server = Server(('0.0.0.0', 8000), d, numthreads=os.cpu_count(), request_queue_size=int(os.cpu_count()/2))
if __name__ == '__main__':
try:
server.start()
except KeyboardInterrupt:
server.stop()
Автор сообщения в блоге https://www.appdynamics.com/blog/engineering/a-performance-analysis-of-python-wsgi-servers-part-2/ может обрабатывать несколько тысяч запросов в секунду на 2-х ядерная машина. Что я делаю неправильно?
Устранение дискового ввода-вывода путем комментирования ведения журнала в приведенном выше примере приложения позволяет мне достигать 666 запросов в секунду. Но не более того. Это все еще мало, учитывая оборудование, на котором я его использую.
Я уже проверил конфигурацию Nginx, и она настроена на гораздо более высокие нагрузки. Я также пробовал отправлять запросы непосредственно на сервер WSGI, пропуская Nginx, и результаты были хуже.