company_banner

Асинхронный Python-код медленнее обычного кода

Автор оригинала: Cal Paterson
  • Перевод
Большинство программистов понимают, что асинхронный Python-код имеет более высокий уровень конкурентности, чем обычный синхронный код. Это даёт некоторые основания полагать, что асинхронный код способен показывать более высокий уровень производительности при решении распространённых задач вроде выдачи динамических веб-страниц или поддержки веб-API.



Но, к сожалению, Python-интерпретатор не выполняет асинхронный код быстрее синхронного.

В реалистичных условиях асинхронные веб-фреймворки показывают немного худшую пропускную способность (выраженную в запросах в секунду), чем обычные, и отличаются гораздо более сильной изменчивостью задержек.

Результаты бенчмарка


Я протестировал широкий набор различных синхронных и асинхронных конфигураций веб-серверов. В следующей таблице показаны результаты испытаний.
Веб-сервер Фреймворк Воркеры Задержки, P50, мс Задержки, P99, мс Пропускная способность,
запросов в секунду
Gunicorn с meinheld Falcon 16 17 31 5589
Gunicorn с meinheld Bottle 16 17 32 5780
UWGGI через http Bottle 16 18 32 5497
UWSGI через http Flask 16 22 33 4431
Gunicorn с meinheld Flask 16 21 35 4510
UWSGI через 'uwsgi' Bottle 16 18 36 5281
UWSGI через http Falcon 16 18 37 5415
Gunicorn Flask 14 28 42 3473
Uvicorn Starlette 5 16 75 4952
AIOHTTP AIOHTTP 5 19 76 4501
Uvicorn Sanic 5 17 85 4687
Gunicorn с gevent Flask 12 24 136 3077
Daphne Starlette 5 20 364 2678

50 и 99 перцентили времени отклика измерены в миллисекундах. Пропускная способность измерена в запросах в секунду. Данные в таблице отсортированы по 99 перцентилю. Я полагаю, что, если речь идёт о реальном применении технологий, это — самый важный показатель.

Обратите внимание на следующее:

  1. Лучше всего показывают себя синхронные фреймворки, однако Flask даёт худшие результаты, чем другие фреймворки.
  2. Худшие результаты имеют все асинхронные фреймворки.
  3. Для асинхронных фреймворков характерна очень сильная изменчивость задержек.
  4. Конфигурации, в которых используется uvloop, альтернативная реализации цикла событий для asyncio, показывают более высокие результаты, чем asyncio-конфигурации без uvloop. Это значит, что если вы вынуждены пользоваться asyncio-решениями, вам стоит применять и uvloop.

Репрезентативны ли результаты этого бенчмарка?


Я думаю да. Я попытался максимально приблизить этот бенчмарк к реальности. Ниже показана схема использованной мной архитектуры тестовой среды.


Архитектура тестовой среды: генератор нагрузки, обратный прокси-сервер, веб-сервер, менеджер соединений, база данных

Я попытался сделать всё возможное для того чтобы как можно точнее смоделировать то, что используется в реальном мире. Здесь имеется обратный прокси (nginx), код, написанный на Python, и база данных (PostgreSQL). Я, кроме того, использовал здесь менеджер соединений для PostgreSQL (PgBouncer), так как полагаю, что в реальных конфигурациях, по крайней мере, там, где используется PostgreSQL, этот менеджер встречается достаточно часто.

Приложение, использованное в испытаниях, запрашивало строку таблицы базы данных с использованием случайного ключа и возвращало данные в формате JSON. Вот репозиторий с полным исходным кодом проекта.

Почему в разных конфигурациях используется разное количество воркеров?


Для того чтобы определить оптимальное количество процессов-воркеров, я пользовался одной и той же простой методикой. А именно, я, исследуя каждую из конфигураций, начинал с одного воркера. Потом я последовательно увеличивал их количество до тех пор, пока производительность конфигурации не ухудшалась.

Оптимальное количество воркеров у асинхронных и синхронных фреймворков различается. У этого есть очевидные причины. Асинхронные фреймворки, так как они используют подсистему ввода-вывода в конкурентном режиме, способны полностью нагрузить работой одно процессорное ядро с помощью одного процесса-воркера.

Этого нельзя сказать о синхронных фреймворках. Когда они выполняют операции ввода-вывода, они захватывают соответствующий ресурс до тех пор, пока эти операции не завершаются. Следовательно, им нужно столько воркеров, чтобы под высокой нагрузкой все имеющиеся в их распоряжении процессорные ядра использовались бы полностью.

Подробнее об этом можно почитать в документации к gunicorn:

Обычно мы рекомендуем, для начала, устанавливать количество воркеров, пользуясь следующей формулой: (2 x $num_cores) + 1. Эта формула построена исходя из предположения о том, что один воркер, выполняющийся на некоем процессорном ядре, будет заниматься чтением данных из сокета или записью данных в сокет, а в это время другой воркер будет заниматься обработкой запроса. За этой формулой не стоят некие глубокие научные исследования.

Характеристики сервера


Я запускал бенчмарк на сервере, соответствующем тарифному плану Hetzner CX31. Речь идёт о 4 vCPU и о 8 Гб памяти. Сервер работал под управлением Ubuntu 20.04. Генератор нагрузки был запущен на другой виртуальной машине (менее мощной).

Почему асинхронный код работает хуже синхронного?


▍Пропускная способность


Если говорить о пропускной способности системы (то есть — о том, сколько запросов она способна обработать в секунду), то главным фактором здесь является не то, синхронный или асинхронный код в ней используется, а то, сколько Python-кода в ней было заменено на нативный код. Проще говоря: чем больше Python-кода, влияющего на производительность, можно заменить нативным кодом, тем лучше. Это — подход к производительности в Python, у которого долгая история (взгляните, например, на numpy).

Meinheld и UWSGI (~5300 запросов в секунду) содержат большие объёмы кода, написанного на C. Стандартный Gunicorn (~3400 запросов в секунду) — это чистый Python.

В связке Uvicorn+Starlette (~4900 запросов в секунду) на более производительный код заменено гораздо больше Python-кода, чем в стандартном сервере AIOHTTP (~4500 запросов в секунду). Правда, тут надо отметить, что AIOHTTP был установлен с необязательными «ускорителями».

▍Задержки


Если говорить о задержках, то тут вырисовывается гораздо более глубокая проблема. Под высокой нагрузкой асинхронные системы работают хуже синхронных. При этом величины задержек асинхронных систем достигают более высоких значений.

Почему это так? Дело в том, что в асинхронном Python-коде реализована кооперативная многопоточность. Это, если объяснить по-простому, означает, что выполнение потоков не прерывается неким менеджером (например — планировщиком ядра). Потоки, вместо этого, отдают ресурсы другим потокам по собственной инициативе. В asyncio для этого используются три команды: await, async for и async with.

Это означает, что время выполнения не распределяется «справедливо», и то, что один поток может случайно лишить процессорного времени другие потоки. Именно поэтому величина задержек в асинхронных системах подвержена большим колебаниям, чем в синхронных.

Традиционные синхронные Python-серверы, вроде UWSGI, используют режим с принудительным распределением ресурсов между процессами. За это отвечает планировщик ядра. Это позволяет обеспечить справедливое распределение ресурсов путём периодического переключения процессов. Это означает, что процессорное время распределяется между процессами более справедливо, что ведёт к меньшей изменчивости величины задержек.

Почему другие бенчмарки дают другие результаты?


Основная масса других бенчмарков (особенно — тех, которые написаны авторами асинхронных фреймворков!) просто не исследует синхронные фреймворки с достаточным количеством воркеров. Это означает, что у исследуемых синхронных фреймворков просто нет возможности задействовать процессоры на полную мощность.

Вот, например, результаты бенчмарка, созданного в рамках проекта Vibora (я этот фреймворк не тестировал, так как он относится к числу наименее популярных решений).


Тестирование фреймворка Vibora

Результаты говорят о том, что пропускная способность Vibora на 500% выше, чем пропускная способность Flask. Правда, когда я почитал код использованного здесь бенчмарка, оказалось, что Flask тут был настроен далеко не самым оптимальным образом: на одно ядро приходился всего один воркер. Когда я это исправил, я получил то, что показано в следующей таблице.
Фреймворк Пропускная способность, запросов в секунду
Flask 11925
Vibora 14148

То есть, на самом деле, Vibora обходит Flask лишь на 18%. Flask — это один из протестированных мной синхронных фреймворков с самой низкой пропускной способностью. Поэтому я полагаю, что более удачная синхронная платформа, несмотря на впечатляющий график, показанный выше, окажется гораздо быстрее, чем Vibora.

Ещё одна проблема заключается в том, что многие бенчмарки отдают приоритет показателям пропускной способности, а не результатам измерения задержек (в бенчмарке Vibora, например, о задержках даже не упоминается). Но, если пропускная способность может быть улучшена благодаря использованию большего количества серверов, задержки, характерные для некоей платформы, работающей под нагрузкой, это не улучшит.

Повышение пропускной способности, на самом деле, приносит пользу только тогда, когда задержки находятся в приемлемом диапазоне значений.

Дальнейшие рассуждения, предположения и результаты наблюдений


Хотя мой бенчмарк и достаточно реалистичен, если рассматривать его через призму используемых в нём технологий, нагрузка, которой я подвергаю сервер, гораздо однороднее той, которой подвергаются реальные серверы. А именно, в моём бенчмарке все запросы вызывают обращения к базе данных, при этом с тем, что пришло из базы данных, всегда делается одно и то же. В реальных приложениях всё, как правило, выглядит иначе. Например, в них могут встречаться быстрые и медленные операции. Некоторые из операций предусматривают интенсивное использование подсистемы ввода-вывода, а некоторые сильно нагружают процессор. Правильным мне кажется исходить из предположения о том (и опыт подсказывает мне, что так оно и есть), что в реальных приложениях диапазон изменения значений задержек будет гораздо больше.

Интуиция подсказывает мне, что в таких условиях ситуация с производительностью асинхронных приложений будет ещё сложнее. Общедоступные результаты наблюдений, сделанных различными людьми, эту идею подтверждают.

Так, Дэн Мак-Кинли писал о своём опыте работы с системой, основанной на фреймворке Twisted и используемой в Etsy. Похоже, что эта система страдала от хронической изменчивости задержек:

[Консультант по Twisted] сказал, что, хотя Twisted отличается высоким уровнем общей пропускной способности, некоторые запросы могут сталкиваться с очень сильными задержками. А это [для систем Etsy] было проблемой, потому что PHP-фронтенд обращался к Twisted сотни или тысячи раз на один веб-запрос.

Майк Байер, автор SQLAlchemy, несколько лет назад написал материал об асинхронном Python-коде и базах данных. Там он рассматривал асинхронный код с несколько иной точки зрения. Он тоже делал бенчмарки и выяснил, что asyncio отличается меньшей эффективностью, чем можно было бы ожидать.

На ресурсе rachelbythebay.com опубликована статья, в которой речь идёт о беспорядке, возникающем от использования конфигураций, основанных на gevent. Хочу отметить, что и я сталкивался с проблемами (хотя и не относящимися к производительности), когда использовал gevent в продакшне.

Ещё кое-что, что мне хотелось бы упомянуть, касается поведения фреймворков во время тестирования. Что интересно, каждый из исследованных мной асинхронных фреймворков давал пренеприятнейшие сбои.

Например, uvicorn останавливал родительский процесс, не останавливая дочерних процессов. А это означало, что мне нужно было «охотиться» за этими процессами, которые всё ещё «держались» за порт 8001. Однажды AIOHTTP выдал внутреннюю критическую ошибку, которая касалась файловых дескрипторов, но не остановился. А это значит, что его не смог бы перезапустить какой-нибудь менеджер процессов. Это, на мой взгляд, прямо-таки смертный грех. Проблемы были и у Daphne, но я уже не помню точно, что именно случилось.

Все эти ошибки случались нечасто, их легко можно было исправить с помощью SIGKILL. Но факт остаётся фактом: не хотелось бы мне столкнуться с чем-то таким в продакшне. В то же время, у меня, например, не было никаких проблем с Gunicorn и UWSGI, за исключением того, что мне сильно не понравилось то, что UWSGI не завершает работу в том случае, если приложение не было правильно загружено.

Итоги


В итоге могу порекомендовать всем тем, кому нужна высокая производительность Python-кода, просто использовать обычные синхронные решения. При этом стоит заменить как можно большие объёмы Python на что-то более быстрое. В случае с веб-серверами, если важна пропускная способность системы, стоит обратить внимание на все фреймворки за исключением Flask. Правда, даже Flask в паре с UWSGI отличается показателями задержек, которые ставят его в один ряд с самыми лучшими фреймворками.

Пользуетесь ли вы серверными решениями, написанными на Python?



RUVDS.com
RUVDS – хостинг VDS/VPS серверов

Комментарии 48

    +4

    Господи, это же треш, а не сравнение. Почему кстати автор не сравнивает один к одному? Что за магические значения 5 и 16?


    Оптимальное количество воркеров у асинхронных и синхронных фреймворков различается. У этого есть очевидные причины.

    Эм… нет

      +6
      Почему в разных конфигурациях используется разное количество воркеров?

      Для того чтобы определить оптимальное количество процессов-воркеров, я пользовался одной и той же простой методикой. А именно, я, исследуя каждую из конфигураций, начинал с одного воркера. Потом я последовательно увеличивал их количество до тех пор, пока производительность конфигурации не ухудшалась.
        –5

        Эта его методика никуда не годится. Человек сравнил теплое с мягким на основе каких-то субъективных метрик.

          +8

          Почему? Человек нашёл максимум производительности в зависимости от количества воркеров. Что не так?

            +1

            Максимум производительности чего?


            Эта методика — чистый подгон под результат. Да, для этого конкретного бессмысленного синтетического теста были найдены оптимальные значения количества воркеров. Если бы эти значения были универсальными — они были бы — сюрприз! — дефолтными и захардкоженными.


            Это все равно, что решать задачу нахождения числа Фибоначчи — доставанием значения из предварительно подготовленного словаря. O(1), прикиньте.


            Кроме того, выполнение атомарных быстрых запросов (испорченных обращением в базу, правда, которая тоже не резиновая, но ладно, простим; на общем фоне это выглядит просто как досадная оплошность) — нивелирует всю ценность асинхронности, потому что в таком сценарии сами воркеры параллелятся.


            Достаточно запустить N тяжелых запросов и следом один легкий на машине с N ядрами, и всем все сразу станет понятно. Ну или бессмертных вебсокетов эдак тысяч сто, и вперед.

              –1

              Максимум производительности приложения, написанного с помощью указанных фреймворков на текущей конфигурации сервера.

                +1

                Неверно.


                Максимум производительности данного конкретного приложения, имеющего столько же общего с любым реальным приложением, сколько сосновая палка с автоматом Калашникова.


                Я даже подробно расписал это в своем предыдущем комментарии, хотя и тогда, и сейчас, мне это представляется очевидным.

      +13
      Шок! Лишние операции замедляют программы! :-D

      А если без шуток, то:

      — Асинхронщина не только для быстродействия нужна.
      — Не увидел в статье информации про количество соединений от воркера к pgbouncer, что сразу ставит под вопрос объективность тестирования.
        +6
        • Профиль нагрузки на сервер.

        Нет, серьёзно, когда приложение лезет в БД, результаты бенчмарков сильно меняются

          0
          Для такого нужно отдельно тестировать ОРМ/Коннектор чтобы получить максимально чистые результаты. Это микрофреймворки, тем более, в них можно что угодно использовать.
          +1
          удален
            0

            В тестах асинхронных фрйемворков используется aiopg

              0
              да. не внимательно глянул
          +16

          Ну как бы предсказуемо. Особенно предсказуемо если ты понимаешь что asyncio — это кооперативный event loop.
          Ну и как бы в каждой нормальной статье или туториале по asyncio в самом начале пишут, для чего он был придуман.


          Предлагаю автору повторить замеры но с измененным профилем нагрузки — не менее 10к долгоживущих HTTP/WebSocket соединений на воркера. Ну или в современных условиях можно и по 100к на воркера. И тогда посмотрим кто кого =)


          Из анналов Stack Overflow:


          if io_bound:
              if io_very_slow:
                  print("Use Asyncio")
              else:
                  print("Use Threads")
          else:
              print("Multi Processing")
            +2
            То ли в девзене то ли в радиоте уже оригинал статьи разнесли в пух и прах.
              0

              В радиоте было

              0
              блин, класс) Зато он блин асинхронный, робяты, вы ж не думали, что все будет бесплатно?
                0

                Хотелось бы увидеть код бенчмарков.

                  +1

                  Всё в посте есть:


                  Вот репозиторий с полным исходным кодом проекта.
                    0

                    Спасибо, не увидел сразу!

                  +1
                  Почему в тестах используется aiopg, а не asyncpg? Количество воркеров тоже крайне спорное.
                    –1

                    Статья попахивает провокацией. Не объяснена объективность количества воркеров, а так же принцип выбора библиотек для Postgres. И к чему тут вообще Postgres если вроде как измеряем производительность web-фреймворков? Почему бы не замерить скорость ответа для статичной html страницы? Или замерить производительность для файлов > 10мб? Вопросов больше чем ответов

                      +4
                      Почему бы не замерить скорость ответа для статичной html страницы?

                      Потому что такой замер будет меньше походить на типичное бекенд приложение, чем замер с несколькими типичными применениями.
                      Но по хорошему стоило бы план тестирования сделать похожим на реальное применение, т.е. преимущественное использование запросов на чтение, изредка запись и периодически сетевые вызовы.


                      Что касается подхода с самым лучшим количеством воркеров, то я его тоже поддерживаю, один к одному дают необъективные показатели, т.к. разные подходы предполагают разное количество воркеров. Синхронные режимы работают лучше при количестве воркеров большем, чем количество ядер.
                      Вот только стоило это оформить более аккуратно, т.е. привести бенчмарк с разными вариантами по количеству и выбрать лучший из них внутри автоматизированного теста, а не захардкоживать эти константы. shpaker

                        0

                        Так он не захардкоживал. Он нашёл оптимальные значения, просто результаты для неоптимальных выкинул.

                          0

                          Пожалуй, объясню почему я написал про один к одному :) Я читал статью за неделю до того как она на хабре появилась и мне казалось, что вполне очевидно, что с ней не так.


                          Про "один к одному" за который в меня только ленивый здесь пальцем не ткнул. Заголовок поста "Асинхронный Python-код медленнее обычного кода" — и тут в общем-то сложно спорить. Можно написать что-то одинаковое и без какого-либо сетевого взаимодействия сравнить лоб в лоб. И мы выясним что и вправду три мешка картошки тяжелее чем один. Тут даже питон не надо знать чтоб понимать, что тест отработает так как написано в заголовке.


                          Но когда в тесте появляются запросы к БД и сам тест это HTTP-сервис, то в общем-то производительность всего теста становится зависимой не только от кода. Да и в общем-то, накинуть тяжелых запросов, и каких-нибудь постоянных соединений и все будет совсем не так однозначно.


                          То есть я к тому, что такой тест вообще, не должен иметь права на жизнь, а больше похож на непонимание того, что и как готовить надо. И тут не важно сколько инстансов ко скольки если брешь в самой методике. Сравнивается то, что так просто нельзя сравнивать. Да и вополне очевидно, что можно написать такой тест на котором асинкайо будет в невыгодном свете ровно как и наоборот.

                            +2
                            то в общем-то производительность всего теста становится зависимой не только от кода

                            Именно так. Но без этих моментов тест потеряет смысл, потому что не будет похож на типичное приложение. Я считаю, что сравнивать надо именно конкретные популярные реализации, а не абстрактные методики.


                            Сравнивается то, что так просто нельзя сравнивать.

                            Не соглашусь. Сравниваются инструменты, которые применительны к задаче типичного API. Но у них разный подход.


                            Да и вополне очевидно, что можно написать такой тест на котором асинкайо будет в невыгодном свете ровно как и наоборот.

                            Задачу, на которой происходит сравнение, надо явно в заголовке указывать и делать выводы, касающиеся только такой задачи, а не обобщать от частного к общему. Тут согласен.

                        +2

                        В статье про измерение производительности нет ни слова про измерение производительности. Идешь в предоставленный код — видишь там ab. Окей, действительно, никто тут производительность и не измерял.

                          +1
                          Вот, например, результаты бенчмарка, созданного в рамках проекта Vibora (я этот фреймворк не тестировал, так как он относится к числу наименее популярных решений).

                          я бы сказал к числу мертвых. ведь в репозитории последний коммит был 17 месяцев назад(и то, редактировался readme. код же не обновлялся два года).
                            0
                            А где gunicorn + aiohttp?
                              +2
                              Здесь явный пример соревнования гоночной феррари формулы один с трактором по бездорожью. И да. В таких условиях действительно, трактор быстрее.
                              Чтобы асинхронный код был производительнее синхронного, необходимо под это настроить с-но инрфраструктуру. В данном конкретном случае, по сути, измеряется производительность базы данных.
                              Реплицируйте базу данных на 10 серверов. Используйте кеш. Думаю результаты будут значительно отличаться от текущих.
                                +5

                                Про репликацию базы согласен, а вот как кэш поможет в случае именно асинка — я не понял.


                                Я воспринимаю этот бенчмарк как попытку показать, что типичные бенчмарки асинхронных фреймворков и есть "соревнования по безодорожью" как вы выразились. К сожалению, многие популярные бенчмарки как раз не учитывают что синхронный фреймворк нельзя гонять в одном воркере или что база не потянет 100000 одновременных коннектов-транзакций.


                                В этом плане этот тест как раз заставляет задуматься о реальном профиле нагрузке и необходимости настройки текущей инфраструктуры, вместо того чтобы бросаться переписывать все на asyncio в надежде получить профит из ничего

                                  0
                                  Смысл кеша, тот же, что и репликации. Т.е. уменьшить нагрузку на бд для получения минимального времени ответа.
                                  В этом плане этот тест как раз заставляет задуматься о реальном профиле нагрузке и необходимости настройки текущей инфраструктуры, вместо того чтобы бросаться переписывать все на asyncio в надежде получить профит из ничего

                                  Тут и спорить не о чем. Каждый инструмент нужен для определенных вещей. Естественно нет смысла переписывать и даже писать с нуля сайтик, генерирующий 3 страницы в минуту с использованием ассинхронщины.
                                    0

                                    Такой кэш и синхронный фреймворк ускорит. Вы предлагали что его важно юзать именно в случае асинка.


                                    Речь не о сайте с 3rpm. А о том, что даже "высокая" нагрузка бывает разной и типичный crud асинк не ускорит (сам по себе)

                                  +1
                                  > В данном конкретном случае, по сути, измеряется производительность базы данных

                                  Почему тогда одна и та же БД с синхронным фронтендом работает быстрее?
                                    0
                                    Psycopg can issue asynchronous queries to a PostgreSQL database. An asynchronous communication style is established passing the parameter async=1 to the connect() function: the returned connection will work in asynchronous mode.

                                    Это цитата из документации psycopg2
                                    Судя по тому, что я вижу в коде, этот драйвер вообще работает в синхронном режиме. Причем это не изменяется и для ассинхронщины. С-но цикл событий блокируется при запросе к бд. И все преимущество сводиться на нет.
                                    pool = psycopg2.pool.SimpleConnectionPool(
                                          1, 4, database="test", user="test", password="test", port=6432,
                                    )
                                    

                                    А должно быть для ассинхронного кода
                                    pool = psycopg2.pool.SimpleConnectionPool(
                                          1, 4, database="test", user="test", password="test", port=6432, async=1
                                    )
                                    
                                      +1
                                      Не внимательно посмотрел. Все таки там используется aiopg.
                                  0

                                  Предлагаю автору запустить рядом с каждым бенчмарком slowhttptest, чтобы понять, почему запускать неасинхронный код в 2020 очень глупо. Спойлер: rps упадет примерно до нуля. И это будет более реально отражать жизнь, т.к. настоящие клиенты где-то на другой стороне довольно медленной сети.

                                    +2

                                    Настоящие клиенты обращаются к nginx, который берёт на себя всю «медленную» работу вместе с буферизацией всего подряд, а к питон-серверу приходит быстрый запрос от 127.0.0.1 или вообще по UNIX-сокету

                                      –1

                                      Слишком много "но" и ограничений, при которых эта схема работает. Например, сразу можно забыть о больших post-запросах, websocket, и многом другом, что не умещается в один буфер. И это если мы вообще говорим о HTTP, вне которого все еще хуже. Можно бесконечно затыкать все эти дыры костялями и различными параметрами в nginx там, где это возможно, а можно просто писать нормальный асинхронный код и не париться.


                                      Дополнительно, если так получается, что неасинхронный код нужно обязательно выкладывать в продакшон под защитой nginx, то и бенчмарки нужно делать с nginx. Тогда как для асинхронных приложений его можно опустить (и использовать более простые и более производительные штуки).

                                        +3

                                        Во-первых, все перечисленные проблемы в nginx решаются (и это вообще не проблемы), а во-вторых, плюшек от nginx НАМНОГО больше чем проблем. Я это когда-то расписывал на ruSO.

                                          0
                                          все перечисленные проблемы в nginx решаются

                                          Лучшее решение любой проблемы — в первую очередь не создавать её себе на ровном месте


                                          плюшек от nginx НАМНОГО больше чем проблем

                                          Прочитал все по диагонали, все перечисленное например решается через traefik, который значительно быстрее.

                                            +5

                                            Ну то есть в итоге вы всё равно предлагаете ставить какой-то дополнительный сервер перед питоном? Тогда на этом можно закончить, потому что я предлагаю ровно то же самое.

                                              0

                                              Предлагаю иметь опции. Все это мне напоминает phpшников, которые 10 лет назад кричали, что apache лучше, чем nginx, потому что, видите ли, у них бенчмарки их php-кода показывают, что с apache быстрее.

                                                0

                                                Я не утверждаю, что должен стоять конкретно nginx (это просто самое популярное решение), но должно стоять хотя бы что-нибудь — хоть тот же traefik (не сильно-то он и slower). Без дополнительного реверс-прокси поднять что-то похожее на реальный продакшен-сервер получится примерно никогда — основной мой посыл вот в этом вот. (Наверное, допишу упоминание traefik в посте на ruSO)

                                                  0

                                                  А что, разве apache mod_php хуже, чем nginx + fpm для чистого php приложения (api без статики, не фуллстек) в 2020?


                                                  Видел бенчмарки, убеждающие в обратном, но там было сравнение на фуллстек приложениях с кучей статики, при этом вывод был сделан от частного к общему.

                                                    0

                                                    К счастью, не могу сказать, потому что уже много лет не видел php нигде. Apache тем более. Но не думаю, что что-то изменилось, nginx сильно лучше.

                                                +3
                                                traefik, который значительно быстрее.

                                                И по вашей же ссылке английским по белому написано:


                                                Traefik is obviously slower than Nginx
                                                  +1

                                                  Внезапно. Ну тогда да, в этом моменте я неправ.

                                      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                      Самое читаемое