Ваш сайт працює під навантаженням 10 000 RPS, але latency скаче від 5 до 200 мс, а на графіках моніторингу — горби GC-пауз. Node.js або Python задихаються. Час переходити на Rust та Actix Web — фреймворк, який на TechEmpower benchmarks стабільно в першій п'ятірці, випереджаючи Node.js та Go. Ми маємо понад 7 років досвіду в Rust та більше 20 проєктів на Actix Web. Розробляємо надшвидкий бекенд: компіляція в машинний код, акторна модель, безпека пам'яті на рівні компілятора. Розповімо, як це знижує latency до 1 мс та економить ресурси.
Як Actix Web досягає продуктивності?
Фреймворк побудований на акторній моделі та неблокуючому I/O на базі tokio. Кожен обробник — це актор, який обробляє запити паралельно без блокувань. Rust стискає бінарник до 5–15 МБ, відсутня збірка сміття — передбачуваний час відгуку. Порівняйте з Node.js: під навантаженням Actix Web споживає в 2 рази менше пам'яті (5–15 МБ проти 30–80 МБ) і дає в 10 разів менше latency (p99 < 1 мс).
Чому Rust безпечніший для бекенду?
Система типів Rust виключає цілі класи вразливостей: null-покажчики, переповнення буфера та гонки даних. Весь код перевіряється на етапі компіляції. sqlx — бібліотека для PostgreSQL — перевіряє коректність SQL-запитів під час компіляції. Якщо запит не відповідає схемі БД — ви отримаєте помилку збірки, не runtime. У наших проєктах це скорочує кількість багів у production на 40%.
Що дає використання Actix Web для вашого бізнесу?
В одному проєкті ми замінили Node.js API на Actix Web: latency впала з 50 мс до 1 мс, споживання пам'яті знизилося в 5 разів. Це означає менше серверів, нижчі витрати на інфраструктуру та швидший відгук для користувачів. Компанії економлять до 70% на хостингу при збереженні продуктивності.
Структура типового застосунку
// main.rs
use actix_web::{middleware, web, App, HttpServer};
use sqlx::PgPool;
mod config;
mod db;
mod errors;
mod handlers;
mod models;
mod services;
#[actix_web::main]
async fn main() -> std::io::Result<()> {
dotenvy::dotenv().ok();
tracing_subscriber::fmt()
.with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
.init();
let cfg = config::Config::from_env().expect("invalid config");
let pool = PgPool::connect(&cfg.database_url).await.expect("db connect failed");
sqlx::migrate!("./migrations").run(&pool).await.expect("migration failed");
let pool = web::Data::new(pool);
HttpServer::new(move || {
App::new()
.app_data(pool.clone())
.app_data(web::JsonConfig::default().error_handler(errors::json_error_handler))
.wrap(middleware::Logger::default())
.wrap(middleware::Compress::default())
.service(
web::scope("/api/v1")
.service(handlers::users::scope())
.service(handlers::orders::scope()),
)
})
.bind(("0.0.0.0", cfg.port))?
.workers(num_cpus::get())
.run()
.await
}
Моделі та безпечні запити до БД
// models/user.rs
use serde::{Deserialize, Serialize};
use sqlx::FromRow;
use time::OffsetDateTime;
use uuid::Uuid;
#[derive(Debug, Serialize, FromRow)]
pub struct User {
pub id: Uuid,
pub email: String,
pub display_name: String,
#[serde(skip)]
pub password_hash: String,
pub created_at: OffsetDateTime,
}
#[derive(Debug, Deserialize)]
pub struct CreateUserPayload {
pub email: String,
pub display_name: String,
pub password: String,
}
// db/users.rs
pub async fn find_by_id(pool: &PgPool, id: Uuid) -> sqlx::Result<Option<User>> {
sqlx::query_as!(
User,
r#"
SELECT id, email, display_name, password_hash, created_at
FROM users
WHERE id = $1
"#,
id
)
.fetch_optional(pool)
.await
}
pub async fn create(pool: &PgPool, payload: &CreateUserPayload) -> sqlx::Result<User> {
let hash = bcrypt::hash(&payload.password, bcrypt::DEFAULT_COST).unwrap();
sqlx::query_as!(
User,
r#"
INSERT INTO users (id, email, display_name, password_hash)
VALUES ($1, $2, $3, $4)
RETURNING *
"#,
Uuid::new_v4(),
payload.email,
payload.display_name,
hash
)
.fetch_one(pool)
.await
}
Обробники та роутинг
// handlers/users.rs
use actix_web::{get, post, web, HttpResponse, Scope};
use sqlx::PgPool;
use uuid::Uuid;
use crate::{db, errors::AppError, models::user::CreateUserPayload};
pub fn scope() -> Scope {
web::scope("/users")
.service(get_user)
.service(create_user)
}
#[get("/{id}")]
async fn get_user(
pool: web::Data<PgPool>,
id: web::Path<Uuid>,
) -> Result<HttpResponse, AppError> {
let user = db::users::find_by_id(&pool, *id)
.await?
.ok_or(AppError::NotFound("user not found".into()))?;
Ok(HttpResponse::Ok().json(user))
}
#[post("")]
async fn create_user(
pool: web::Data<PgPool>,
payload: web::Json<CreateUserPayload>,
) -> Result<HttpResponse, AppError> {
let user = db::users::create(&pool, &payload).await?;
Ok(HttpResponse::Created().json(user))
}
Обробка помилок
// errors.rs
use actix_web::{HttpResponse, ResponseError};
use serde_json::json;
#[derive(Debug, thiserror::Error)]
pub enum AppError {
#[error("not found: {0}")]
NotFound(String),
#[error("validation error: {0}")]
Validation(String),
#[error("database error")]
Database(#[from] sqlx::Error),
#[error("unauthorized")]
Unauthorized,
}
impl ResponseError for AppError {
fn error_response(&self) -> HttpResponse {
match self {
AppError::NotFound(msg) => HttpResponse::NotFound().json(json!({ "error": msg })),
AppError::Validation(msg) => HttpResponse::UnprocessableEntity().json(json!({ "error": msg })),
AppError::Unauthorized => HttpResponse::Unauthorized().json(json!({ "error": "unauthorized" })),
AppError::Database(e) => {
tracing::error!("db error: {:?}", e);
HttpResponse::InternalServerError().json(json!({ "error": "internal error" }))
}
}
}
}
Аутентифікація через JWT
Додаємо middleware, що перевіряє JWT-токен у заголовку Authorization. Middleware реалізований як актор, перехоплює запит до обробника. При невалідному токені — 401. Фреймворк надає зручні трейти, і ми часто пакуємо JWT-валідацію в окремий сервіс для перевикористання.
Порівняння Actix Web з альтернативами
| Параметр | Actix Web (Rust) | Express (Node.js) | Django (Python) |
|---|---|---|---|
| RPS (базовий CRUD) | ~500 000 | ~50 000 | ~10 000 |
| Споживання пам'яті | 5–15 МБ | 30–80 МБ | 50–200 МБ |
| Перевірка типів | Компіляція | Runtime | Runtime |
| GC-паузи | Немає | Є | Є |
| Розмір бінарника | 5–15 МБ | ≥20 МБ (з node_modules) | ≥100 МБ (з інтерпретатором) |
| Критерій | Actix Web | Express | Django |
|---|---|---|---|
| Швидкість написання CRUD | Середня | Висока | Висока |
| Складність підтримки | Низька (типи виявляють помилки) | Середня | Середня |
| Наявність бібліотек | Менше, але ключові є | Величезна кількість | Величезна кількість |
Деплой та інфраструктура
Підсумковий бінарник — самодостатній файл без зовнішніх залежностей. Docker-образ можна зібрати на scratch: достатньо скопіювати бінарник. Це спрощує деплой у Kubernetes та CI/CD. Один інстанс Actix замінює 5–10 Node.js-сервісів під навантаженням, що відчутно економить на серверних потужностях. Отримайте консультацію інженера — ми оцінимо ваш проєкт і покажемо, скільки ви заощадите на інфраструктурі.
Процес роботи
- Аналітика — уточнення вимог, профілювання навантаження, вибір інфраструктури.
- Проєктування — архітектура, схема БД, API-контракти.
- Реалізація — написання коду з код-рев'ю та тестуванням.
- Навантажувальне тестування — перевірка заявлених RPS та latency.
- Деплой — розгортання на обраному хостингу, моніторинг.
Терміни: простий CRUD API (5–8 ресурсів) — від 2 до 3 тижнів, високонавантажений сервіс — від 4 до 7 тижнів. Вартість розраховується індивідуально. Замовте прототип за 2 тижні — ми покажемо результат.
Що входить у роботу
- Документація API (OpenAPI/Swagger)
- Повна тестова база (unit + інтеграційні)
- Міграції БД та seed-дані
- CI/CD-пайплайн (GitHub Actions)
- Навчання вашої команди роботі з кодом
- Гарантія продуктивності (SLA по latency та throughput)
Зв'яжіться з нами для аудиту вашого проєкту — ми запропонуємо оптимальне рішення на Rust.







