Переломный момент: когда Python перестал справляться
В начале 2025 года LogiTrans — средняя логистическая компания, обрабатывающая более 50 000 запросов на отслеживание посылок в минуту, — столкнулась с пределом. Их стек микросервисов, построенный на Python (FastAPI + Celery), страдал от всплесков задержек и частых простоев. Только сервис отслеживания дважды в неделю испытывал 15-минутные сбои, что обходилось примерно в 12 000 долларов в час из-за штрафов за нарушение SLA и расходов на перенаправление.
Я был частью инженерной команды, принявшей радикальное решение: переписать основные сервисы отслеживания и оптимизации маршрутов на Rust с использованием Actix-web, пройдя обучение по курсу Asibiont Rust for Web. Вот наша история — что мы сделали, как мы это сделали и какие конкретные цифры заставили улыбнуться технического директора.
Проблемы Python
У наших микросервисов на Python было три основных проблемы:
- GIL и накладные расходы async: FastAPI использует asyncio, но под нагрузкой GIL всё равно вызывал конкуренцию. P99 задержки наших запросов составлял 250 мс в часы пик, что значительно превышало SLA в 50 мс.
- Потребление памяти: Каждый рабочий процесс Python потреблял около 120 МБ. При 50 репликах это 6 ГБ только для сервиса отслеживания. При скачках трафика (например, в Чёрную пятницу) автоскейлинг добавлял новые экземпляры, которые прогревались 15–20 секунд — слишком медленно.
- Холодный старт в serverless: У нас был меньший серверный компонент на AWS Lambda, где холодный старт Python в среднем составлял 3 секунды. Это вечность для обновлений в реальном времени.
У нашей команды был опыт работы с Go, но мы хотели чего-то с более строгой безопасностью памяти и абстракциями с нулевой стоимостью. Тогда мы открыли для себя Rust и Actix-web.
Почему Rust + Actix?
Rust обеспечивает безопасность памяти без сборщика мусора, абстракции с нулевой стоимостью и бесстрашную конкурентность. Actix-web — это высокопроизводительный фреймворк на основе акторов, который стабильно лидирует в бенчмарках (см. TechEmpower Web Framework Benchmarks — Actix занимает первое место во многих тестах).
Ключевые преимущества для нашего логистического сценария:
- Нет пауз GC: предсказуемая задержка, критически важная для отслеживания в реальном времени.
- Эффективный async: среда выполнения tokio с чрезвычайно низкими накладными расходами.
- Проверки на этапе компиляции: обнаружение ошибок памяти (use-after-free, гонки данных) до продакшена.
- Маленький бинарник, быстрый запуск: типичный сервис на Actix запускается менее чем за секунду.
Путь обучения: курс Asibiont Rust for Web
Никто из нашей команды не имел опыта работы с Rust. Нам нужен был практический курс, ориентированный на веб-разработку, а не только на синтаксис. Оценив несколько вариантов, мы выбрали курс Asibiont Rust for Web, потому что:
- Он текстовый с уроками, генерируемыми ИИ, что позволяет учиться в своём темпе без отвлекающих видео.
- Учебная программа напрямую охватывает Actix-web, async/await, SQLx для баз данных и интеграцию с очередями сообщений — именно то, что нам было нужно.
- ИИ-тьютор (не чат в реальном времени, а генерирует контекстно-зависимые уроки) отвечал на наши вопросы о модели владения Rust применительно к веб-запросам.
За 6 недель наша команда из 5 инженеров прошла курс, создав в качестве финального проекта имитацию сервиса отслеживания. К 4-й неделе у нас был прототип, который сравнялся по пропускной способности с Python. К 6-й неделе мы были готовы переписывать продакшен.
Миграция: шаг за шагом
Этап 1: Определение правильных сервисов
Мы не переписывали всё. Только сервисы приёма отслеживания и оптимизации маршрутов — наиболее чувствительные к задержкам. Бэк-офисные CRUD-приложения остались на Python.
Этап 2: Перенос основной логики
Вот упрощённый пример изменения конечной точки:
Python (FastAPI) – обновление отслеживания:
@app.post("/tracking/event")
async def handle_event(event: TrackingEvent):
# Обработка и сохранение
await db.store(event)
return {"status": "ok"}
Rust (Actix-web) – та же конечная точка:
#[post("/tracking/event")]
async fn handle_event(event: web::Json<TrackingEvent>, pool: web::Data<PgPool>) -> impl Responder {
sqlx::query("INSERT INTO events (id, lat, lng, time) VALUES ($1, $2, $3, $4)")
.bind(event.id)
.bind(event.lat)
.bind(event.lng)
.bind(event.time)
.execute(pool.get_ref())
.await
.unwrap(); // упрощённо; в продакшене использовали правильную обработку ошибок
HttpResponse::Ok().json(json!({"status": "ok"}))
}
Обратите внимание на безопасность на этапе компиляции: если бы мы забыли обработать ошибку базы данных, код не скомпилировался бы (если только мы не использовали .unwrap(), чего в продакшене не делали).
Этап 3: Тестирование и нагрузочное тестирование
Мы использовали wrk и locust для сравнения производительности. Результаты оказались ошеломляющими.
| Метрика | Python (FastAPI) | Rust (Actix) | Улучшение |
|---|---|---|---|
| P50 задержка | 12 мс | 2.1 мс | в 5.7 раза быстрее |
| P99 задержка | 250 мс | 8 мс | в 31 раз быстрее |
| Пропускная способность (запросов/с) | 3 200 | 24 000 | в 7.5 раза больше |
| Память на один экземпляр | 120 МБ | 18 МБ | снижение на 85% |
| Время запуска | 15 с (контейнер) | 0.8 с (контейнер) | в 18 раз быстрее |
Источник: наше внутреннее нагрузочное тестирование с 100 одновременными соединениями на идентичном оборудовании (8 vCPU, 16 ГБ RAM, PostgreSQL 15).
Этап 4: Постепенное развёртывание
Мы развернули сервисы на Rust за обратным прокси NGINX, начав с 10% трафика. Через две недели без инцидентов перешли на 100%.
Результаты: доступность 99,99%
С момента полной миграции в июне 2025 года наш сервис отслеживания достиг доступности 99,993% — это менее часа общего простоя за 12 месяцев. Для сравнения: предыдущие 99,7% (22 часа простоя за тот же период).
Другие ощутимые достижения:
- Затраты на инфраструктуру снизились на 55%: мы сократили количество реплик сервиса отслеживания с 50 до 8.
- Уровень ошибок упал с 0,8% до 0,02%: строгая обработка ошибок в Rust устранила большинство исключений времени выполнения.
- Скорость разработки возросла: после периода обучения (около 3 месяцев) наша команда теперь выпускает функции на Rust быстрее, чем на Python — благодаря тому, что компилятор ловит ошибки на ранних этапах.
Уроки, которые мы извлекли
- Сначала инвестируйте в обучение. Курс Asibiont дал нам структурированный путь. Без него кривая обучения была бы вдвое длиннее.
- Не переписывайте всё. Сосредоточьтесь на 20% сервисов, которые вызывают 80% проблем.
- Модель владения Rust окупается. Как только вы её осваиваете, вы пишете надёжный код с первого раза.
- Actix-web готов к продакшену. Мы используем его уже больше года без единого сбоя, вызванного фреймворком.
Стоит ли вашей компании делать это?
Если вы запускаете чувствительные к задержкам веб-сервисы на Python и сталкиваетесь с ограничениями производительности, Rust + Actix — проверенный путь к улучшению. Инвестиции в обучение реальны, но такие инструменты, как курс Asibiont Rust for Web, делают его доступным даже для команд, начинающих с нуля.
Наш технический директор теперь шутит, что миграция на Rust была лучшим решением, которое мы приняли, — и я с этим согласен. Доступность 99,99% говорит сама за себя.
Комментарии