Главная/ Блог/ Python под нагрузкой

Как использовать Python в высоконагруженных сервисах

Python медленный, это известное убеждение. Но что, если мы скажем, что при должной оптимизации и его можно разогнать до показателей Go? В этом тексте мы на примере одного из наших проектов расскажем, как адаптировать Python к нагрузкам.

July 2024

Плюсы и минусы Python для больших проектов

Python — это интерпретируемый язык, что по умолчанию делает его медленнее компилируемых языков. Плюс, в CPython есть GIL, который предотвращает выполнение нескольких потоков Python одновременно. Это ограничивает возможности многопоточной обработки на многоядерных процессорах. Python предлагает высокоуровневые абстракции, которые упрощают разработку, но могут добавлять издержки на производительность по сравнению с низкоуровневыми языками. А динамическая типизация означает дополнительные затраты на проверку типов, замедляющие выполнение кода.

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

Что же можно сделать? Во-первых, использовать С. В виде расширений или за счет Cython, который позволяет пользоваться статической типизацией на важных участках кода. Во-вторых, можно использовать JIT-компиляторы типа PyPy. В третьих, кэширование. Вовремя включенное кэширование может на порядок улучшить производительность бэкенда. 

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

Задача

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

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

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

Поиск решения

Какой язык выбрать под backend высоконагруженного сервиса? Основной стек клиента — Python. Поэтому мы решили не отказываться от него сразу и сравнить его с другими языками. В шорт-лист вошли Go, Python и Rust. 

Для каждого нам предстояло подобрать фреймворк, который бы минимизировал наши трудозатраты. Для Python мы протестировали AIOHTTP, Litestar, Robyn, но остановились на Granian — фреймворке, написанном на Rust. После оптимизации именно это решение помогло нам добиться нужной производительности.

Для тестирования производительности мы написали четыре сервиса: на Go, Rust, нативном Python и оптимизированном Python c Granian. 

Тестирование

Чтобы удостовериться, что наше решение может выдержать 100 тысяч запросов в секунду, нужно было создать стенд, который генерировал бы такую нагрузку. Для нагрузочного тестирования мы выбрали Locust, написанный, кстати, на Python. 

Чтобы иметь возможность динамически подключать нагружаемые ресурсы, инфраструктуру развернули в кластере Kubernetes в облаке и автоматизировали все с помощью Terraform. 

Для каждого тест-сервиса подготовили Docker-образы и написали сценарии тестирования. Сценарии включали разное количество пользователей — от 100 до максимального количества, которое сможет дать Locust. 

Результаты нас довольно сильно удивили. На одном ядре Rust удалось разогнать до 14 тысяч запросов в секунду, Go до 5,7 тысяч запросов в секунду, а оптимизированный Python до 5,5 тысяч запросов в секунду. Полноценное тестирование производительности на нативном Python мы не проводили, даже локально он показывал слишком скромный результат. 

Защита концепции

На демо-созвоне с клиентом мы в режиме реального времени показали тесты всех четырех решений, и предложили использовать именно решение на оптимизированном Python. Вот его основные плюсы:

  • Производительность на уровне Go. 
  • Клиент останется в привычном стеке.
  • Не нужно усложнять архитектуру вводом строго типизированных языков для динамических правил. 
  • Обслуживать и развивать решение могут Python-разработчики уровня middle. 
  • Экономическая эффективность за счет оптимального сетапа и более доступных на рынке Python-специалистов. 

Решение на Rust мы отмели сразу же, поскольку в России почти нет коммерческой Rust-разработки, и соответственно, очень мало специалистов. В мире Rust в основном используют для крипто-проектов или крупнейших облачных платформ типа Google Cloud и AWS. С Go похожая ситуация, квалифицированных специалистов мало, они дороже, и в целом разработка будет довольно дорогой.

Заключение

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

 

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

Будем на связи
Прикрепить файл
Максимальный размер файла: 8 МБ.
Допустимые типы файлов: jpg jpeg png txt rtf pdf doc docx ppt pptx.