Axum vs Actix Web vs Rocket для реальной работы с Rust API
Axum vs Actix Web vs Rocket: сравните поведение рантайма, совместимость с библиотеками и читаемость кода, чтобы выбрать Rust API-фреймворк не только по таблицам.

Почему графики бенчмарков не показывают реальный выбор
Бенчмарк, который возвращает hello world из одного маршрута, говорит очень мало о том API, который ваша команда будет поддерживать в следующем месяце. Настоящие эндпоинты делают больше работы. Они парсят JSON, проверяют auth, пишут логи, обращаются к базе данных, иногда трогают cache и превращают ошибки в ответы, которыми люди действительно могут пользоваться.
Именно поэтому выбор между Axum, Actix Web и Rocket редко сводится только к сырой скорости запросов. Небольшой тест может показать разницу в накладных расходах, но он скрывает то, что влияет на поведение в продакшене. Когда маршрутизация становится глубже, а middleware наслаивается, фреймворк — лишь одна часть общей стоимости.
Команды платят ещё и временем на отладку. Если один фреймворк экономит 2 мс в микробенчмарке, но потом вы тратите два лишних дня на поиск причины таймаута, для большинства компаний это плохая сделка. Часто самая дорогая часть — это время инженеров, которое уходит на распутывание обработчиков, состояния, extractors, типов ошибок и порядка middleware.
Представьте обычный маршрут API. Пользователь отправляет токен, приложение проверяет права, загружает аккаунт, пишет audit event и возвращает JSON. В таком потоке запросов вызовы базы и логика приложения обычно важнее, чем скриншоты из бенчмарка. Фреймворк всё равно важен, но уже по другим причинам.
Лучше сравнивать по трём вещам: поведению под реальной нагрузкой, совместимости с библиотеками и подходами, которые вы уже используете, и читаемости кода через шесть месяцев, когда кто-то будет это менять. Обычно именно эта комбинация решает, будет Rust API-фреймворк спокойным или раздражающим в повседневной работе. Быстрые графики — это хорошо. Понятный код, надёжные инструменты и предсказуемое поведение обычно экономят больше денег.
Что означает поведение рантайма на практике
Реальные API почти не живут в идеальных условиях. Они стартуют после деплоя, работают в памяти днями, обращаются к базе данных, ждут ответы от других сервисов и получают трафик не ровной линией, а всплесками.
Поэтому скриншоты с бенчмарками показывают лишь часть картины. Полезный вопрос не в том, "кто победил в синтетическом тесте?" Полезный вопрос в том, что произойдёт, когда приложение станет загруженным, зависимость замедлится, а вам всё равно понадобятся понятные ошибки и предсказуемая latency.
Время запуска важно, если вы часто деплоите, запускаете короткоживущие задачи или используете serverless-подходы. Потребление памяти важно, если несколько сервисов работают весь день. Для многих команд память со временем обходится дороже, чем экономия пары микросекунд на простом маршруте.
Всплески подключений — это место, где проявляются слабые стороны. Фреймворк может выглядеть быстрым при ровной нагрузке, а потом вести себя совсем иначе, когда много клиентов одновременно переподключаются после сетевого сбоя. Вам нужны лимиты, таймауты и поведение очередей, которые подходят вашему приложению, а не просто быстрый hello world.
Middleware сильнее влияет на поведение рантайма, чем многие ожидают. Каждый запрос проходит через логирование, auth, tracing, compression, rate limits и иногда CORS. Если этот поток легко читать, команды обычно раньше замечают ошибки. Если extraction запросов и обработка ошибок выглядят предсказуемо, обычная нагрузка остаётся скучной, а скука — это хорошо.
Небольшой пример делает это понятнее. Допустим, эндпоинт проверяет токен, загружает пользователя из Postgres, а затем вызывает billing API. Если billing API зависнет на 4 секунды, приложение быстро упадёт с полезным таймаутом или продолжит накапливать работу, пока задержка не расползётся повсюду? Здесь важен backpressure. Важна и отмена. Важен и способ, которым фреймворк переводит внутренние ошибки в ответы.
В staging стоит смотреть на несколько простых сигналов: cold start после деплоя, память спустя 30–60 минут, p95 latency во время внезапного всплеска и поведение запросов, когда один downstream-сервис замедляется. Обычно эти числа говорят больше, чем график с одним победителем. Они показывают, как фреймворк ведёт себя в том приложении, которое вы действительно строите.
Как ощущается Axum в реальных проектах
Axum часто ощущается спокойным и предсказуемым, когда сервис вырастает за пределы нескольких маршрутов. Обработчик обычно выглядит как обычный async Rust: принимает типизированные входные данные, возвращает типизированный ответ, а бизнес-логику держит в середине. Это упрощает code review, потому что детали запроса видны в сигнатуре функции, а не спрятаны в макросах или скрытом состоянии.
Typed extractors — важная часть этого опыта. Если маршруту нужен path id, JSON body, auth data и shared state, вы видите всё это заранее. Небольшой обработчик вроде create_user(Path(id), State(app), Json(payload)) сразу многое рассказывает следующему читателю ещё до того, как он прочитает тело функции. Такая явность хорошо стареет.
Axum также естественно подходит командам, которые уже используют Tokio и crates на базе Tower. Middleware здесь в основном строится как Tower layers, поэтому логирование, проверки auth, таймауты запросов, rate limits и shared state следуют подходам, которые уже существуют в более широком мире async Rust. Если ваше приложение одновременно запускает workers, queues или фоновые задачи на Tokio, Axum обычно подходит без особого трения.
При этом Axum не всегда самый простой для чтения с первого взгляда. Когда накапливаются несколько слоёв, custom extractors и generic response types, type machinery может стать тяжёлой. Код остаётся честным, но новым членам команды может понадобиться больше времени, чтобы понять, что означает каждый bound и где возникла ошибка.
В повседневной работе Axum обычно читается лучше всего, когда обработчики остаются небольшими, а команда договаривается о нескольких общих паттернах. Если ваша группа любит явный код и уже живёт в экосистеме Tokio, Axum часто ощущается как наименее неожиданное решение.
Как ощущается Actix Web в реальных проектах
Actix Web часто ощущается как полноценный web-toolkit, а не просто тонкий слой маршрутизации. Вы можете настраивать маршруты через App::new(), группировать их с помощью scope() и подключать handlers так, чтобы API оставался читаемым по мере роста.
Сигнатуры обработчиков прямые, когда команда привыкает к ним. Функция может принимать web::Path, web::Query, web::Json, состояние запроса через web::Data и даже сырой HttpRequest, когда это нужно. Это делает зависимости заметными, потому что они стоят прямо в сигнатуре функции.
Модель состояния приложения практична. Обычно вы создаёте shared state один раз, оборачиваете его в web::Data и передаёте в handlers. Для реального сервиса это может быть пул базы данных, конфиг, feature flags и клиент для другого API. Это простой паттерн, и он хорошо масштабируется, если команда держит объекты состояния небольшими и понятными.
Middleware тоже находится близко к структуре приложения. Вы можете оборачивать всё приложение, scope или отдельный service. Это даёт пространство для auth, логирования, request IDs и rate limits, не пряча их в отдельный слой, который новым коллегам потом приходится искать.
Extractors — одна из сильных идей Actix Web. Они убирают парсинг из handlers, поэтому код маршрута остаётся сосредоточенным на бизнес-логике, а не на подготовке запроса. Это особенно полезно, когда API начинает повторять одни и те же проверки и настройку на десятках эндпоинтов.
Actix Web обычно сильнее всего ощущается, когда команда хочет зрелый HTTP-стек и уже знает его подходы. Компромисс в том, что проект довольно быстро может начать жить по своим собственным правилам. Если большая часть вашего стека тяготеет к соглашениям Tokio и Tower, Actix может ощущаться немного более отдельным от остальной codebase.
Как ощущается Rocket в реальных проектах
Rocket чувствуется opinionated — и многим Rust-разработчикам это действительно нравится. Его макро-ориентированный стиль делает маршруты простыми для просмотра, потому что путь, метод и handler находятся прямо перед вами. Файл Rocket часто читается как аккуратная карта API, а не как куча проводов.
Эта читаемость в ежедневной работе важнее, чем многие признают. Когда кто-то приходит в небольшой проект или возвращается к нему спустя месяц, он обычно быстро находит маршрут и понимает входные данные. Меньше времени уходит на то, чтобы искать код настройки по половине codebase.
Rocket также убирает много boilerplate благодаря request guards, typed forms и встроенным шаблонам для типичных web-задач. Проверки auth, shared state, cookies, headers и разобранные данные запроса могут попадать в handler так, что это выглядит аккуратно, а не вычурно. Для небольшой admin-панели или startup back-office API это помогает держать проект компактным.
Команда из двух или трёх человек часто выигрывает от такой структуры. Все следуют одному и тому же пути, handlers получаются похожими, а решения реже превращаются в долгие споры. Если приложение в основном обслуживает формы, JSON, auth и несколько фоновых задач, Rocket может ощущаться спокойным и продуктивным.
Компромисс становится заметным, когда приложение перестаёт укладываться в предпочтительную форму Rocket. Если вам нужны нестандартные цепочки middleware, необычные потоки запросов или очень точный контроль над низкоуровневым поведением, Rocket может ощущаться более жёстким, чем Axum. Некоторым командам это нравится — до первой нестандартной задачи.
Совместимость с экосистемой тоже важна. Если ваш стек опирается на crates и примеры, построенные вокруг паттернов Tower, Rocket может ощущаться чуть в стороне. С ним по-прежнему можно собрать хороший API, но интеграция иногда требует больше размышлений, чем в Axum. Часто это и есть настоящая граница: Rocket отлично ощущается, когда вам нравятся его правила, и заметно хуже — когда остальной Rust-стек хочет других правил.
Как совместимость с экосистемой меняет ответ
Настоящий выбор часто сводится к менее яркому вопросу: что уже окружает ваш API. Фреймворк может понравиться в первый день и всё равно отнимать время позже, если auth, доступ к базе, tracing, фоновые задачи и тесты требуют отдельного glue-кода.
Axum часто лучше всего подходит, когда команда уже использует инструменты из мира Tokio-heavy. Tower middleware, SQLx, tracing, WebSockets и отдельные worker-процессы обычно сочетаются там с меньшим трением. Если ваше приложение уже опирается на распространённые паттерны async Rust, Axum обычно упрощает code review, потому что большинству разработчиков Rust понятна форма такого кода.
Actix Web тоже может отлично работать, но стоит проверять не только синтаксис handlers. Посмотрите, как ваш auth flow подключается, как shared state проходит через приложение и сколько адаптеров нужно для tracing или тестового окружения. Если команда всё время пишет обёртки только для того, чтобы другие библиотеки выглядели нормально, эта цена будет заметна каждую неделю.
Rocket часто производит самое приятное первое впечатление. Routes и request guards читаются очень хорошо. Через полгода это имеет значение только в том случае, если тестирование, документация, деплой и поддержка по-прежнему проходят гладко. Здесь важен и найм. Обычно проще нанимать под подходы, которые уже знают многие Rust-разработчики, чем под фреймворк с более строгими соглашениями.
Полезно задать короткий набор вопросов. Подходит ли ваш auth crate к модели запросов и middleware, которую вы хотите? Естественно ли выглядит слой базы данных и в handlers, и в тестах? Остаются ли логи, tracing и request IDs одинаковыми во всех сервисах? Совпадают ли health checks и поведение shutdown с тем, как вы деплоите? Должны ли фоновые задачи жить внутри приложения или в отдельном worker?
Команды часто упускают скучные вещи. Формат логов, локальная настройка, генерация OpenAPI, сборка контейнеров и поведение shutdown потом определяют, насколько удобно работать с поддержкой. Если один фреймворк заставляет делать слишком много исключений в деплое или on-call-работе, его аккуратный синтаксис перестаёт казаться таким аккуратным.
Простой сценарий для команды
Представьте стартап из пяти человек: два backend-инженера, один frontend-инженер, дизайнер и основатель, который каждую неделю меняет приоритеты. Им нужны логин, CRUD-экраны, загрузка файлов, rate limits и небольшой admin-раздел до первого пилота с клиентом через восемь недель.
Вот здесь выбор фреймворка перестаёт быть спором о бенчмарках и превращается в вопрос ресурсов команды. Команда выбирает не в вакууме. Она выбирает то, что сможет выпускать, отлаживать и продолжать менять без выгорания.
Если эта команда выбирает Axum, причина, скорее всего, в понятности при изменениях. Axum хорошо сочетается с экосистемой Tokio, а его маршрутизация, extractors и middleware обычно ощущаются прямолинейно. Когда основатель в понедельник просит SSO, а в четверг — audit logs, такой стиль помогает.
Если они выбирают Actix Web, причина, скорее всего, в уверенности в зрелом и быстром HTTP-стеке и в том, что в команде есть человек, который уже знает его подходы. Это важнее, чем скриншот с requests per second. Знакомый инструмент может сэкономить неделю проб и ошибок, а на коротком дедлайне это огромная разница.
Если они выбирают Rocket, причина, скорее всего, в комфорте разработчиков. Rocket может ощущаться аккуратным и читаемым для стандартных API и admin-инструментов, особенно когда команде нужны строгие рамки и меньше boilerplate в коде маршрутов.
Теперь изменим одну деталь: один инженер отвечает за operations и on-call. Этот человек занимается деплоями, логами, инцидентами и ночными исправлениями. Тогда выбор часто смещается в сторону Axum, потому что более широкая совместимость с экосистемой облегчает подключение tracing, auth-слоёв, rate limiting и кастомных частей без борьбы с фреймворком.
Actix Web всё ещё имеет смысл, если у того же инженера большой опыт именно с ним. Rocket становится более тяжёлым выбором, если приложение продолжает расти в необычных направлениях, потому что запросы на изменения редко остаются аккуратными надолго.
Маленьким командам обычно лучше всего подходит фреймворк, который можно прочитать в 2 часа ночи, а не тот, который выигрывает с небольшим отрывом на графике.
Как выбирать шаг за шагом
Начните со следующих 90 дней, а не с графика бенчмарков. Большинство команд принимают лучшие решения, когда оценивают фреймворк по работе, которую им действительно нужно выпускать: auth, validation, фоновые задачи, логи, тесты и несколько неприятных edge cases.
Сначала выпишите API-функции, которые вы ожидаете построить в первые три месяца. Достаточно простого списка: login, один CRUD-ресурс, pagination, file upload, shared app state и одна фоновая задача — этого хватит для честной проверки.
Затем соберите одно и то же маленькое API во всех трёх фреймворках. Используйте одинаковые маршруты, одинаковые обращения к базе и одинаковые сценарии ошибок, чтобы сравнивать фреймворк, а не меняющийся дизайн.
Запустите каждую версию на своей машине и посмотрите на скучные вещи. Проверьте использование памяти после нескольких прогонов, прочитайте логи, сымитируйте плохие запросы и посмотрите, сколько труда нужно, чтобы вернуть аккуратные ошибки.
Короткие тесты говорят очень многое. Если один фреймворк делает интеграционные тесты неудобными или логи выглядят беспорядочно, когда что-то ломается, это трение будет повторяться и позже.
Дальше дайте код двум коллегам без каких-либо заметок по настройке, кроме инструкции, как запустить. Спросите, какая версия понятнее всего, где они застряли и какой вариант они доверили бы расширять в следующем месяце.
Выбирайте тот фреймворк, чьи подходы всё ещё выглядят аккуратно после добавления ещё одной функции. Хороший выбор — тот, который команда сможет развивать без переписывания handlers, типов ошибок или структуры проекта каждые несколько недель.
Небольшой пример помогает. Если ваша проверка включает auth, проверку запросов и один admin-маршрут, обратите внимание на то, где код остаётся спокойным. Самый быстрый фреймворк на бумаге всё равно может замедлить команду, если простые изменения расползаются по шести файлам и двум кастомным абстракциям.
Ошибки, которые приводят к плохому выбору
Быстрая картинка из бенчмарка может подтолкнуть команду к неправильному решению. График почти ничего не говорит о коде, который вы будете писать каждый день. Он не показывает, как передаётся состояние приложения, как устроены ошибки, как наслаивается middleware и как вы будете дебажить запрос, который ломается посередине auth и validation.
Одна распространённая ошибка — оценивать фреймворк до того, как вы соберёте один реальный маршрут со скучными частями. Добавьте auth, request IDs, tracing, конфиг, доступ к базе и аккуратный ответ с ошибкой. Тогда различия проявятся быстро. Одни инструменты кажутся гладкими, пока вам не нужен shared state между handlers. Другие выглядят аккуратно в демо, а потом становятся неловкими, когда extractors и custom errors начинают расползаться по codebase.
Ещё одна ловушка — доверять старому туториалу. Rust web-библиотеки быстро меняются. Руководство двухлетней давности может подталкивать вас к подходам, которые сегодня кажутся тяжёлыми, или к предупреждениям, которых уже не существовало, когда этот материал писали. Если сравниваете эти фреймворки, сравнивайте текущие версии и современные привычки сообщества, а не устаревшие примеры.
Соответствие команде важнее любимого инструмента одного эксперта. Если один сильный Rust-разработчик выбирает фреймворк, а всем остальным потом его поддерживать, цена проявится позже. Новые люди читают код, тесты нужно обновлять, а простые функции начинают занимать больше времени, чем должны.
Короткая проверка обычно быстро показывает плохой выбор. Соберите один защищённый эндпоинт, добавьте shared state, верните два-три реальных сценария ошибок, напишите один integration test и дайте другому коллеге просмотреть код.
Самая большая ошибка — гнаться за скоростью первой недели и игнорировать читаемость через шесть месяцев. Фреймворк, который экономит два часа в первый день, потом может стоить дней, если поток handler-ов кажется непонятным или обработка ошибок превращается в россыпь преобразований. Простое API, которое команда может читать, менять и отлаживать, выигрывает чаще, чем умная настройка, которая хорошо смотрелась на скриншоте бенчмарка.
Это решение становится лучше, когда его тестирует команда, которой потом с ним жить. Обычно это фильтр полезнее, чем сырые цифры.
Быстрые проверки перед тем, как выбрать
Прежде чем окончательно выбрать фреймворк, проведите маленький тест, похожий на ваше реальное API, а не на игрушечное демо. Соберите три маршрута: один публичный read-endpoint, один защищённый write-endpoint и один маршрут, который специально падает. За этот час вы узнаете больше, чем из любого скриншота с бенчмарком.
Несколько проверок помогают отсеять плохие варианты. Попросите коллегу пройти один запрос от router до handler и обратно к ответу. Если после одного просмотра он теряется, код будет трудно поддерживать. Отправьте битый payload и посмотрите логи. Понятные ошибки, request IDs и полезный tracing важнее, чем быстрый happy path. Добавьте auth, validation и два теста. Если вы копируете одну и ту же настройку в каждый endpoint, эта цена быстро растёт. Потом представьте codebase с 50 маршрутами и тремя разработчиками, которые трогают его каждую неделю. Фреймворк всё ещё должен выглядеть спокойным и предсказуемым.
Маленькие команды ощущают это первыми. Когда один человек в ту же неделю занимается продуктовой работой, исправлениями backend и деплоем, запутанный поток запросов отнимает реальное время. Фреймворк, который экономит 20 миллисекунд на графике, но стоит 20 минут на отладку, — не лучший выбор.
Здесь вкус уже менее важен, чем поведение под нагрузкой. Если Actix даёт вам скорость, но команда всё время спрашивает, где живёт state, это реальное трение. Если Rocket сначала выглядит аккуратно, но вы постоянно подстраиваете приложение под его стиль, на это стоит обратить внимание. Если Axum хорошо сочетается с вашими привычками tracing и middleware, это часто выигрывает в ежедневной работе.
Ещё одна последняя проверка помогает: пусть другой разработчик исправит баг с плохим запросом без подсказок. Если он сможет найти handler, понять ошибку и добавить тест за один подход, скорее всего, вы сделали хороший выбор.
Следующие шаги, если нужна вторая точка зрения
Большинству команд не нужна ещё одна неделя споров. Им нужен короткий тест, который покажет, как фреймворк ведёт себя с их auth, кодом базы данных, обработкой ошибок и настройкой деплоя. Пятидневный spike обычно говорит больше, чем стопка скриншотов с бенчмарками.
Запустите один реальный поток эндпоинта в том фреймворке, к которому вы склоняетесь. Включите проверку запроса, один вызов базы данных, логирование, тесты и то, как ваша команда будет запускать это в production. Именно здесь компромиссы перестают быть теорией.
Перед стартом запишите, почему этот фреймворк кажется лучшим выбором, что вы будете измерять, какие компромиссы уже готовы принять и когда пересмотрите решение. Эта заметка помогает больше, чем люди ожидают. Через шесть недель можно будет проверить, движется ли команда спокойно или просто терпит боль, потому что решение уже принято.
Если выбор повлияет на найм, расходы на облако или более широкую платформенную архитектуру, может помочь внешний разбор. Oleg Sotnikov at oleg.is работает как fractional CTO и startup advisor, а его работа охватывает архитектуру, инфраструктуру и AI-first development. Для команд, принимающих более широкое платформенное решение, короткий разбор может быть полезнее, чем ещё один раунд споров о фреймворках.
Выбирайте Rust API-фреймворк, который ваша команда сможет читать, отлаживать и поддерживать без постоянного трения. Если код остаётся понятным, а деплои — скучными, скорее всего, вы приняли правильное решение.
Часто задаваемые вопросы
Полезны ли графики бенчмарков вообще?
Нет. Бенчмарки полезны, но они показывают лишь узкий фрагмент реальной работы. Используйте их как небольшой сигнал, а затем проверьте auth, обращения к базе, логи, таймауты и обработку ошибок в маленьком приложении, похожем на ваше API.
Когда стоит выбрать Axum?
Выбирайте Axum, если ваша команда уже использует инструменты в стиле Tokio и хочет явный код обработчиков. С ним обычно легко работать по мере роста маршрутов, middleware и фоновых задач, хотя для новичков в Rust типы могут казаться тяжёлыми.
Когда Actix Web подходит лучше всего?
Actix Web подходит, когда вам нужен зрелый HTTP-стек и кто-то в команде уже знает его подходы. Маршрутизация, extractors и модель состояния приложения хорошо работают в реальных сервисах, но он может ощущаться немного отдельно от остальной codebase на базе Tokio.
Подходит ли Rocket для startup API?
Rocket хорошо работает для маленьких команд, которым нужны читаемые маршруты и строгие соглашения. Он часто нравится для admin-панелей, back-office API и типовых CRUD-задач, но становится менее удобным, когда нужен необычный поток middleware или более низкоуровневый контроль.
Что стоит измерять кроме сырой скорости?
Следите за cold start после деплоя, использованием памяти после реального трафика, p95 latency во время всплеска и поведением запросов, когда один downstream-сервис замедляется. Эти цифры показывают, как приложение будет ощущаться в продакшене, гораздо лучше, чем простой график requests per second.
Почему middleware и обработка ошибок так важны?
Middleware и поток ошибок сильнее влияют на ежедневную работу, чем ожидает большинство команд. Если auth, логирование, tracing, rate limits и понятные ответы об ошибках легко читать, команда быстрее исправляет баги и реже ошибается под давлением.
Стоит ли маленькой команде выбирать понятность вместо скорости?
Для большинства маленьких команд понятность важнее. Фреймворк, который экономит немного latency, но замедляет ревью, тестирование и отладку, обычно обходится дороже, чем кажется.
Как быстро сравнить Axum, Actix Web и Rocket?
Соберите одно и то же небольшое API во всех трёх фреймворках и оставьте одинаковыми маршруты, обращения к базе и сценарии ошибок. Затем попросите двух коллег прочитать код, запустить тесты и внести одно небольшое изменение. Вариант, который они поймут быстрее, обычно многое показывает.
Какие ошибки приводят к неправильному выбору фреймворка?
Команды обычно ошибаются, когда судят по игрушечным демо, копируют старые туториалы или позволяют одному эксперту выбирать за всех. Соберите один реальный защищённый маршрут, добавьте shared state и тесты и посмотрите, где код начинает сопротивляться.
Когда стоит получить второе мнение?
Если этот выбор повлияет на найм, расходы на облако, привычки деплоя или более широкую платформенную стратегию, короткий внешний разбор может сэкономить время. Опытный CTO может заметить скрытые затраты в tracing, тестировании, инфраструктуре и соответствии команды ещё до того, как вы окончательно решите.