Ваш сайт працює під навантаженням 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.







