Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Алексей Созыкин. Asynchronous frameworks battle

Алексей Созыкин. Asynchronous frameworks battle

Все мы любим, когда наше приложение работает быстро, обрабатывает много запросов, и код для него приятно и удобно писать. В асинхронном мире python для этих целей существуют такие фреймворки как tornado, aiohttp и sanic. Но так ли они хороши как популярны? Есть ли другие инструменты, способные с ними потягаться? За счет каких опций одни предпочтительней других? В своем докладе я постараюсь дать ответы на эти вопросы.

More Decks by Python Community Chelyabinsk

Other Decks in Technology

Transcript

  1. Benchmarks config CPU: AMD FX(™) 6300 3.5Ghz 1 core, MEM:

    2Gb, Ubuntu 18.04 Повышаем число соединений, пока не упремся в 80ms на персентиле 90. Как тестируем wrk -d300s -c100 -t1 --latency
  2. aiohttp • Не совсем framework • Миллион расширений (asyncpg, aiohttp_swagger,

    aiohttp_cors…) • Streaming responses • Websockets • Сигналы • Поддержка Context Variables (3.7 python) • ready for prod
  3. Vibora • Cython • Свой шаблонизатор (похож на jinja2) •

    Свои схемы (асинхронные) • Поддержка Streaming responses • Type hints components • Не готов к проду (Заново переписывается)
  4. Vibora type hints components from vibora import Vibora, Request, Response

    from vibora import Route class Config: def __init__(self): self.name = 'Vibora Component' app = Vibora() app.add_component(Config()) @app.route('/') async def handler(request: Request, config: Config): current_config = request.get_component(Config) assert current_config is config return Response(config.name)
  5. Результаты RPS p50 p90 p99 aiohttp c100 4324 22.14ms 25.42ms

    39.41ms vibora c100 32867 2.89ms 3.76ms 9.48ms aiohttp cmax(300) 4012 71.45ms 84.64ms 103.38ms vibora cmax(1000) 30489 31.57ms 46.02ms 63.6ms
  6. Tornado • Python • Streaming responses • Websockets • Свой

    шаблонизатор • Ready for prod • Ветеран труда
  7. Starlette • Python • Под капотом ASGI (uvicorn, hypercorn, daphne)

    • Не имеет жестких зависимостей. Опционально: ◦ aiofiles для FileResponse и StaticFiles ◦ jinja2 для Jinja2Templates ◦ ujson для UJSONResponse • CORS, GZip, Streaming responses • Ready for prod
  8. Результаты RPS p50 p90 p99 tornado c100 1875 51.7ms 57.93ms

    73.29ms starlette c100 9674 9.8ms 11.16ms 22.44ms tornado cmax(140) 1844 73.28ms 83.81ms 101.52ms starlette cmax(500) 8625 50.22ms 87.02ms 122.54ms
  9. Sanic • Python • Свой вебсервер на uvloop + httptools

    • Не поддерживает HTTP2 • Поддерживает Websockets • Поддерживает стримы (request, response, file) • Нет встроенного шаблонизатора (есть jinja2-sanic) • Ready for prod
  10. Quart • Python • Под капотом ASGI • Асинхронная версия

    Flask (можно мигрировать с Flask) • Поддерживает Websockets и HTTP2 • Streaming responses • Шаблонизатор jinja2 • Ready for prod
  11. Результаты RPS p50 p90 p99 quart c100 2476 38.57ms 43.93ms

    73.07ms sanic c100 9976 9.4ms 11.12ms 22.33ms quart cmax(150) 2228 61.01ms 80.58ms 145.75ms sanic cmax(500) 9247 45.79ms 81.42ms 108.3ms
  12. japronto • C • uvloop + picohttpparser • Конвейерный HTTP

    сервер • Поддержка Streaming responses • Не готов к проду
  13. FastAPI • Python • Под капотом ASGI (uvicorn, hypercorn, daphne)

    • Starlette + pydantic • Валидация данных на type hints • SwaggerUI • Ready for prod
  14. Результаты RPS p50 p90 p99 japronto c100 28627 3.34ms 3.7ms

    7.57ms fastapi c100 7033 13.71ms 14.89ms 24.69ms japronto cmax(1000) 27920 30.05ms 51.16ms 71.47ms fastapi cmax(350) 6470 49.68ms 72.86ms 107.72ms
  15. Как тестируем echo "request" | vegeta attack -duration 300s -rate

    300 pyflame -p PID -s 300 | flamegraph.pl > result.svg
  16. • async/await интерфейс (можно и в trio, и в curio)

    • Общие серверные реализации (uvicorn, hypercorn, daphne, etc.) • Общие middlwares • Явное отделение сервера от приложения Немного про ASGI
  17. async def read_body(receive): body = b'' more_body = True while

    more_body: message = await receive() body += message.get('body', b'') more_body = message.get('more_body', False) return body Пример async def app(scope, receive, send): body = await read_body(receive) await send({ 'type': 'http.response.start', 'status': 200, 'headers': [ [b'content-type', b'text/plain'], ] }) await send({ 'type': 'http.response.body', 'body': body, })
  18. Аргументы: • scope - словарь, содержащий информацию о входящем соединении

    • receive - канал, по которому можно получать входящие сообщения с сервера • send - канал, по которому отправляются исходящие сообщения на сервер
  19. Scope { 'type': 'http', 'scheme': 'http', 'root_path': '', 'server': ('127.0.0.1',

    8000), 'http_version': '1.1', 'method': 'GET', 'path': '/', 'headers': [ [b'host', b'127.0.0.1:8000'], [b'user-agent', b'curl/7.51.0'], [b'accept', b'*/*'] ] }