Почему Rust — это ваш следующий шаг в микросервисах?
Мир микросервисов долгое время был поделен между Java (со своим Spring Boot), Python (FastAPI, Django) и Go. Но последние пару лет всё чаще слышен голос Rust. Почему? Потому что Rust даёт то, что раньше казалось недостижимым: производительность C++ и безопасность памяти — без сборщика мусора.
Если вы пишете на Python, вы привыкли к скорости разработки. Но когда нагрузка растёт, Python начинает «тормозить». Rust решает эту проблему радикально: один микросервис на Rust может заменить 3–4 сервиса на Python по производительности. И при этом код не «сыпется» в рантайме с NullPointerException.
В этой статье мы не будем философствовать. Мы возьмём и напишем микросервис на Rust с нуля. Вы увидите, что это не страшно, а очень даже увлекательно.
Готовим окружение: что нужно установить
Прежде чем писать код, давайте убедимся, что у вас есть всё необходимое. Если вы новичок в Rust — не пугайтесь, установка занимает 2 минуты.
1. Установка Rust
Самый простой способ — через официальный установщик rustup:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
После установки проверьте версию:
rustc --version
cargo --version
2. Выбор редактора
Рекомендую VS Code с расширением rust-analyzer. Это даст вам автодополнение, подсветку ошибок и рефакторинг «из коробки».
3. Docker (для контейнеризации)
Микросервисы обычно живут в контейнерах. Установите Docker Desktop или Docker Engine. Мы будем собирать образ в конце статьи.
Выбираем фреймворк: Actix-web vs Axum vs Rocket
В мире Rust есть три основных фреймворка для HTTP-серверов. Вот их краткое сравнение:
- Actix-web — самый быстрый (до 1 млн запросов/сек). Зрелый, много примеров. Идеален для продакшена.
- Axum — современный, от создателей Tokio. Лучше всего интегрируется с async-экосистемой.
- Rocket — самый «дружелюбный» синтаксис, но медленнее обновляется.
В этой статье мы будем использовать Actix-web, так как он проверен годами и имеет наибольшее комьюнити. Но если вы хотите «модный» стек — смело берите Axum.
Первый микросервис: сервис сокращения ссылок
Чтобы не быть абстрактными, напишем реальный микросервис — url-shortener. Он будет принимать длинный URL и возвращать короткий.
Шаг 1. Создаём проект
cargo new url-shortener
cd url-shortener
Шаг 2. Добавляем зависимости
Откройте Cargo.toml и добавьте:
[dependencies]
actix-web = "4"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
uuid = { version = "1", features = ["v4"] }
tokio = { version = "1", features = ["full"] }
Шаг 3. Пишем код
Замените содержимое src/main.rs на следующий код:
use actix_web::{web, App, HttpServer, HttpResponse, Responder};
use serde::{Deserialize, Serialize};
use std::sync::Mutex;
use uuid::Uuid;
// Структура для хранения ссылок в памяти
struct AppState {
links: Mutex>,
}
#[derive(Serialize, Deserialize, Clone)]
struct Link {
id: String,
url: String,
}
#[derive(Deserialize)]
struct CreateLinkRequest {
url: String,
}
// POST /shorten — создаёт короткую ссылку
async fn shorten(data: web::Data<AppState>, body: web::Json<CreateLinkRequest>) -> impl Responder {
let new_link = Link {
id: Uuid::new_v4().to_string()[..8].to_string(), // берём первые 8 символов UUID
url: body.url.clone(),
};
let mut links = data.links.lock().unwrap();
links.push(new_link.clone());
HttpResponse::Created().json(&new_link)
}
// GET /{id} — редирект на оригинальный URL
async fn redirect(data: web::Data<AppState>, path: web::Path<String>) -> impl Responder {
let id = path.into_inner();
let links = data.links.lock().unwrap();
if let Some(link) = links.iter().find(|l| l.id == id) {
HttpResponse::Found()
.append_header(("Location", link.url.clone()))
.finish()
} else {
HttpResponse::NotFound().body("Link not found")
}
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let data = web::Data::new(AppState {
links: Mutex::new(Vec::new()),
});
println!("Server running at http://127.0.0.1:8080");
HttpServer::new(move || {
App::new()
.app_data(data.clone())
.route("/shorten", web::post().to(shorten))
.route("/{id}", web::get().to(redirect))
})
.bind(("127.0.0.1", 8080))?
.run()
.await
}
Шаг 4. Запускаем и тестируем
cargo run
В другом терминале:
# Создаём короткую ссылку
curl -X POST http://127.0.0.1:8080/shorten -H "Content-Type: application/json" -d '{"url": "https://pythonlib.ru"}'
# Ответ: {"id":"a1b2c3d4","url":"https://pythonlib.ru"}
# Переходим по короткой ссылке
curl -v http://127.0.0.1:8080/a1b2c3d4
Вы увидите редирект (HTTP 302) на сайт pythonlib.ru. Работает! 🎉
Добавляем асинхронность и базу данных
Хранить данные в памяти — это несерьёзно для микросервиса. Давайте подключим PostgreSQL и SQLx (асинхронный драйвер).
Добавляем SQLx в Cargo.toml
sqlx = { version = "0.7", features = ["runtime-tokio-rustls", "postgres", "uuid"] }
Создаём таблицу
CREATE TABLE links (
id VARCHAR(8) PRIMARY KEY,
url TEXT NOT NULL,
created_at TIMESTAMP DEFAULT NOW()
);
Обновляем код для работы с БД
Блоги
Рекомендаций по книгам
Читайте также
Чистый код на Java: принципы Clean Code с примерами
JDK и JRE: в чем разница? Разбор популярного вопроса новичков
Service Mesh на Rust: как микросервисы становятся быстрее и безопаснее
CQRS и Event Sourcing в JavaScript: архитектура для сложных систем
Serverless на Java: AWS Lambda и альтернативы для бэкенда