API Gateway для Rust сервисов: Строим умный вход в микросервисную архитектуру

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение: Почему Rust — идеальный выбор для API Gateway

Представьте себе швейцара элитного отеля. Он не просто открывает дверь — он проверяет бронь, направляет гостей в нужные номера, вызывает лифт и передает багаж. В мире микросервисов эту роль выполняет API Gateway. А если вы строите высоконагруженную систему на Rust, то ваш швейцар должен быть не просто вежливым, а молниеносным и неутомимым.

Rust с его нулевой стоимостью абстракций и безопасностью памяти становится стандартом для создания критически важных сетевых компонентов. Сегодня мы разберем, как построить собственный API Gateway для Rust сервисов, который будет обрабатывать тысячи запросов в секунду без единого утечки памяти.

В этой статье мы не просто поговорим о теории — я покажу вам рабочий код на Actix Web, который вы сможете адаптировать под свои задачи. Готовы? Поехали!

Что такое API Gateway и зачем он нужен в Rust-экосистеме?

API Gateway — это единая точка входа для всех клиентских запросов к вашим микросервисам. Представьте, что у вас есть сервис авторизации, сервис пользователей и сервис заказов. Вместо того чтобы клиент знал адреса всех трех, он стучится только в Gateway, а тот уже маршрутизирует запросы.

Основные задачи API Gateway:

  • Маршрутизация — перенаправление запросов к нужным сервисам
  • Балансировка нагрузки — распределение трафика между инстансами
  • Аутентификация и авторизация — проверка JWT, API ключей
  • Rate Limiting — защита от DDoS и перегрузок
  • Логирование и мониторинг — сбор метрик по всем запросам

Почему именно Rust? Потому что традиционные решения на Node.js или Python могут не справиться с нагрузкой, а Go хоть и быстр, но не дает таких гарантий безопасности памяти. Rust позволяет писать высокопроизводительные прокси с предсказуемым потреблением ресурсов.

Проектируем архитектуру: от простого к сложному

Прежде чем писать код, давайте определимся с архитектурой нашего API Gateway. Мы построим его на фреймворке Actix Web — одном из самых быстрых HTTP-фреймворков в мире Rust.

Компоненты нашего Gateway:

  1. HTTP-сервер для приема запросов
  2. Маршрутизатор на основе URL-путей
  3. Прокси-клиент для перенаправления запросов
  4. Middleware для аутентификации
  5. Rate limiter для защиты
  6. Механизм health check для сервисов

Пишем код: базовый API Gateway на Rust с Actix Web

Давайте создадим минимальный, но функциональный API Gateway. Начнем с настройки проекта:

cargo new api-gateway --bin
cd api-gateway
cargo add actix-web actix-rt reqwest serde serde_json tokio

Шаг 1: Базовая структура сервера

Создадим файл src/main.rs с простым сервером, который слушает порт 8080:

use actix_web::{web, App, HttpServer, HttpRequest, HttpResponse, middleware};
use std::collections::HashMap;

// Структура для хранения конфигурации маршрутов
struct AppConfig {
    routes: HashMap<String, String>, // путь -> адрес сервиса
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    // Настраиваем маршруты: /users -> сервис пользователей, /orders -> сервис заказов
    let mut routes = HashMap::new();
    routes.insert("/users".to_string(), "http://localhost:8081".to_string());
    routes.insert("/orders".to_string(), "http://localhost:8082".to_string());

    let config = web::Data::new(AppConfig { routes });

    println!("API Gateway запущен на порту 8080");

    HttpServer::new(move || {
        App::new()
            .app_data(config.clone())
            .wrap(middleware::Logger::default()) // Логируем все запросы
            .default_service(web::to(gateway_handler))
    })
    .bind("127.0.0.1:8080")?
    .run()
    .await
}

Шаг 2: Обработчик запросов с проксированием

Теперь самое интересное — функция-маршрутизатор, которая перенаправляет запросы к нужным сервисам:

use reqwest::Client;
use actix_web::HttpResponse;

async fn gateway_handler(
    req: HttpRequest,
    body: web::Bytes,
    config: web::Data<AppConfig>,
) -> HttpResponse {
    // Получаем путь запроса (например, /users/profile)
    let path = req.path().to_string();
    let client = Client::new();

    // Ищем подходящий маршрут
    for (route_prefix, target_url) in &config.routes {
        if path.starts_with(route_prefix) {
            // Формируем URL для перенаправления
            let target = format!("{}{}", target_url, &path[route_prefix.len()..]);

            // Копируем заголовки (кроме Host)
            let mut headers = reqwest::header::HeaderMap::new();
            for (key, value) in req.headers().iter() {
                if key != "host" {
                    headers.insert(key.clone(), value.clone());
                }
            }

            // Отправляем запрос к целевому сервису
            match client
                .request(req.method().clone(), &target)
                .headers(headers)
                .body(body.to_vec())
                .send()
                .await
            {
                Ok(response) => {
                    let status = response.status();
                    let response_body = response.bytes().await.unwrap_or_default();
                    return HttpResponse::build(status)
                        .content_type("application/json")
                        .body(response_body);
                }
                Err(e) => {
                    return HttpResponse::InternalServerError()
                        .json(serde_json::json!({
                            "error": format!("Ошибка соединения с сервисом: {}", e)
                        }));
                }
            }
        }
    }

    // Если маршрут не найден
    HttpResponse::NotFound()
        .json(serde_json::json!({
            "error": "Маршрут не найден"
        }))
}

Этот код делает главное — он динамически маршрутизирует запросы. Если приходит GET /users/123, Gateway перенаправляет его на http://localhost:8081/123. Красиво, правда?

Добавляем аутентификацию: Middleware для JWT

Настоящий API Gateway не может быть без защиты. Добавим middleware, который проверяет JWT-токены перед тем, как пустить запрос к сервисам:

use actix_web::dev::{Service, ServiceRequest, ServiceResponse, Transform};
use actix_web::Error;
use futures::future::{ok, Ready};
use std::task::{Context, Poll};

pub struct AuthMiddleware;

impl<S, B> Transform<S> for AuthMiddleware
where
    S: Service<Request = ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
    S::Future: 'static,
    B: 'static,
{
    type Request = ServiceRequest;
    type Response = ServiceResponse<B>;
    type Error = Error;
    type Transform = AuthMiddlewareService<S>;
    type InitError = ();                

Блоги

Рекомендаций по книгам