Главная / Курсы / Rust: бэкенд на Axum / Глава 1. Введение в Axum / Что такое Axum и его особенности
# Глава 1.1. Что такое Axum и его особенности Axum — это веб-фреймворк для создания HTTP-сервисов на Rust. Его разрабатывает [команда Tokio](https://github.com/tokio-rs). Tokio — самый популярный асинхронный рантайм для Rust, то есть библиотека, которая выполняет асинхронный код и берёт на себя работу с сетью. Сам Axum построен поверх [hyper](https://habr.com/ru/articles/917936/) — низкоуровневой библиотеки, которая реализует протокол HTTP: разбирает входящие запросы и формирует ответы. Axum отвечает за то, что происходит с запросом после его разбора: за маршрутизацию, то есть выбор функции-обработчика по пути и методу запроса, за извлечение данных из запроса и за формирование ответа. Всё остальное он делегирует другим библиотекам экосистемы Tokio. ## Почему Axum Axum создан командой Tokio и тесно интегрирован с экосистемой вокруг этого проекта. Он не реализует HTTP-сервер целиком сам: работа распределена между несколькими библиотеками, и каждая отвечает за свой слой: | Крейт | Слой | За что отвечает | | ------- | ------------------- | ---------------------------------------------------------------------------- | | `tokio` | Асинхронный рантайм | Выполнение асинхронных задач, работа с сетью и таймерами | | `hyper` | HTTP | Реализация протоколов HTTP/1.1 и HTTP/2 | | `tower` | Промежуточные слои | Инфраструктура, служебные слои и отказоустойчивость сервисов | | `axum` | Веб-фреймворк | Маршрутизация запросов, их обработчики и извлечение данных из запросов | Промежуточный слой (middleware) — это код, который выполняется до или после обработчика для каждого запроса. Например, он может записать запрос в лог, проверить авторизацию или ограничить время ответа. Это важно для понимания: Axum не реализует весь HTTP-стек самостоятельно, а использует готовые компоненты экосистемы. Его задача — предоставить удобные абстракции для написания REST API. При этом промежуточные слои из `tower` можно использовать и без Axum, а при необходимости можно работать с HTTP на более низком уровне через `hyper`. ## Плюсы Axum - Большая экосистема. Axum интегрирован с экосистемой Tokio, поэтому его легко сочетать с нужными библиотеками. Например, промежуточные слои из `tower` работают с Axum без адаптеров. - Меньше ошибок во время выполнения. Многие ошибки обнаруживаются на этапе компиляции благодаря системе типов Rust. - Мало магии. Axum не прячет устройство приложения за большим количеством макросов. Основные части сервера представлены обычными структурами, функциями и трейтами. ## Альтернативы - **Actix Web** — главный конкурент Axum и самый зрелый фреймворк в экосистеме. Он долгие годы возглавляет бенчмарки, обладает богатой функциональностью, объявляет маршруты через макросы и построен на акторной модели. Из минусов — собственная экосистема. Кроме того, репутация проекта пострадала из-за большого количества `unsafe` в ранних версиях и конфликта вокруг [мейнтейнера](https://habr.com/ru/articles/484436/). Сейчас код давно вычищен, но осадок в интернете остался. - **Rocket** — более дружелюбный к новичкам фреймворк с максимумом магии на макросах, похожий на Flask из Python. Хорошо подходит для обучения и прототипов. Из минусов: он долгое время требовал ночную сборку компилятора, релизы выходят редко, экосистема заметно меньше, а магия макросов мешает, когда нужно нестандартное поведение. - **Loco** — фреймворк в духе Ruby on Rails, построенный поверх Axum и рассчитанный на создание приложения целиком, а не только API. Он предоставляет больше готовой инфраструктуры и задаёт структуру приложения, но пока недостаточно зрелый для промышленной эксплуатации. ## Первое приложение Создаём проект: ```bash cargo new hello-axum cd hello-axum ``` Добавляем зависимости: ```bash cargo add axum cargo add tokio --features macros,rt-multi-thread ``` Tokio разбит на отключаемые части — фичи (features), чтобы в сборку попадало только то, что действительно используется. Нам нужны две фичи: `macros` добавляет атрибут `#[tokio::main]`, а `rt-multi-thread` подключает многопоточный планировщик задач, который этот атрибут использует по умолчанию. Без `rt-multi-thread` проект не соберётся: ```text error: The default runtime flavor is `multi_thread`, but the `rt-multi-thread` feature is disabled. ``` Работу с сетью отдельно подключать не нужно: её включает сам Axum. Теперь создадим HTTP-сервер: ```rust // src/main.rs use axum::{routing::get, Router}; use tokio::net::TcpListener; #[tokio::main] async fn main() { // запросы GET / будет обрабатывать функция hello let router = Router::new().route("/", get(hello)); let listener = TcpListener::bind("127.0.0.1:3000") .await .unwrap(); println!("Слушаем на http://{}", listener.local_addr().unwrap()); axum::serve(listener, router).await.unwrap(); } async fn hello() -> &'static str { "Hello, World!" } ``` Разберём, что здесь происходит. Функция `main` — точка входа в программу, и асинхронной она быть не может. Вызов асинхронной функции не выполняет её код, а лишь возвращает `Future` — описание работы, которую только предстоит сделать. Чтобы эта работа началась, нужен асинхронный рантайм, который будет её выполнять. В стандартной библиотеке Rust рантайма нет, и это сделано намеренно: серверу, микроконтроллеру и браузерному приложению нужны разные рантаймы, поэтому язык оставляет выбор разработчику. Точку входа вызывает операционная система, и она должна быть обычной функцией, поэтому объявление `async fn main` без атрибута не скомпилируется. Атрибут `#[tokio::main]` превращает нашу асинхронную `main` в обычную, которая создаёт рантайм Tokio и запускает в нём код. При желании то же самое можно написать вручную через [`tokio::runtime::Builder`](https://docs.rs/tokio/latest/tokio/runtime/struct.Builder.html). `Router` отвечает за распределение запросов: он хранит таблицу соответствий между маршрутами и обработчиками. Дальше в курсе обработчик запроса будем называть хендлером. Метод `route` принимает путь и указывает, какая функция обработает запросы к нему, а функция `get` уточняет, что это касается только HTTP-метода GET. Функция `hello` передаётся без круглых скобок, потому что мы не вызываем её сами, а передаём Axum, чтобы он вызывал её на каждый подходящий запрос. `TcpListener` отвечает за сетевое подключение: он занимает адрес `127.0.0.1:3000` и принимает входящие соединения. Обратите внимание, что мы импортируем `tokio::net::TcpListener`, а не `std::net::TcpListener` из стандартной библиотеки. Версия из стандартной библиотеки на время ожидания клиента блокирует поток операционной системы: пока соединение не придёт, поток больше ничего не делает. В асинхронном приложении это недопустимо, потому что на одном потоке рантайм выполняет множество задач, и заблокированный поток остановил бы их все. Версия из Tokio на время ожидания возвращает управление рантайму, и тот продолжает выполнять другие задачи. `axum::serve` запускает асинхронный HTTP-сервер, который связывает сетевой адрес с обработчиками запросов. Он принимает соединения от `TcpListener`, с помощью hyper разбирает пришедшие по ним HTTP-запросы и передаёт их в `Router`, который выбирает подходящий обработчик. Функция `hello` — это обработчик маршрута. Она возвращает строку, а Axum сам превращает её в HTTP-ответ и выставляет нужные заголовки. Как это устроено, разберём в главе 1.4. Метод `unwrap` извлекает значение из `Result` или завершает программу с ошибкой, например если порт уже занят. Для учебного примера это допустимо, но в реальном приложении ошибки обрабатывают явно. Подробнее об обработке ошибок поговорим в главе 1.7. Запускаем: ```bash cargo run ``` Проверяем: ```bash $ curl -i http://127.0.0.1:3000/ HTTP/1.1 200 OK content-type: text/plain; charset=utf-8 content-length: 13 date: ... Hello, World! ``` Сервер ответил статусом `200 OK` и вернул нашу строку. Заголовки `content-type` и `content-length` мы нигде не указывали: их выставил Axum. Мы запустили сервер с одним маршрутом. Почти каждый реальный обработчик принимает и возвращает данные в формате JSON, поэтому в следующей главе разберёмся, как Rust-структуры превращаются в JSON и обратно.
Наша группа в telegram. Задавайте вопросы и общайтесь.
Задонатить. Поддержите развитие площадки.