Flask о масштабируемости сигары и пушки
Я разработал конечную точку HTTP API, используя flask, которая принимает данные 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-anaлиз-of-python-wsgi-servers-part-2/ может обслуживать несколько тысяч запросов в секунду на 2-ядерная машина. Что я делаю не так?
Устранение дискового ввода-вывода путем комментирования входа в систему в приведенном выше примере приложения позволило мне достичь 666 запросов в секунду. Но не более того. Это все еще мало, учитывая оборудование, на котором я его запускаю.
Я уже проверил конфигурацию Nginx, и она настроена на гораздо более высокие нагрузки. Я также пробовал отправлять запросы непосредственно на сервер WSGI, пропуская Nginx, и результаты были хуже.