API Gateway для C++ сервисов: когда производительность решает всё
Представьте: у вас десяток микросервисов на C++, каждый — настоящий зверь производительности. Клиенты шлют тысячи запросов в секунду, а система начинает тормозить из-за... шлюза. Знакомая боль? Чаще всего разработчики тянут в C++-экосистему Nginx с Lua или тяжеловесный Kong, хотя могли бы написать свой API Gateway на C++ — быстрый, легкий и предсказуемый.
В этой статье я покажу, как построить свой шлюз API на C++ с нуля. Мы разберем архитектуру, напишем рабочий код на чистом C++ и сравним его с популярными решениями. Вы узнаете, когда свой API Gateway — это оправданный выбор, а когда лучше взять готовое.
Зачем писать API Gateway на C++?
Давайте честно: большинство проектов не нуждаются в кастомном шлюзе. Nginx, Envoy или Kong решают 90% задач. Но есть сценарии, где C++ незаменим:
- Задержки менее 1 мс — финансовые системы, гейм-серверы, real-time трейдинг
- Миллионы RPS — высоконагруженные API, CDN, IoT-платформы
- Жесткие требования к памяти — embedded-системы, edge-устройства
- Тесная интеграция с C++-кодом — вызов нативных библиотек без накладных расходов
Кроме того, API Gateway C++ дает полный контроль над каждым байтом. Вы сами решаете, как обрабатывать заголовки, как кешировать, как балансировать нагрузку. Никакой магии — только предсказуемая производительность.
Архитектура API Gateway на C++
Типичный шлюз API на C++ состоит из нескольких слоев:
- Транспортный слой — прием и отправка HTTP-запросов (boost::beast, libcurl, собственный сокет)
- Маршрутизатор — сопоставление URL с бэкенд-сервисами
- Middleware — аутентификация, rate limiting, логирование, CORS
- Балансировщик — round-robin, least-connections, взвешенный
- Кеш — in-memory кеш ответов (LRU, LFU)
Вот как это выглядит схематично:
Client → [API Gateway C++] → Middleware → Router → Balancer → Backend A
↓
[Cache]
Каждый слой — это отдельный модуль. Их можно компоновать как конструктор. Хотите добавить JWT-валидацию? Пишете middleware. Нужен новый алгоритм балансировки? Добавляете класс.
Пишем API Gateway на C++: боевой пример
Возьмем современный C++20 и библиотеку boost::beast для HTTP. Наш шлюз будет:
- Принимать запросы на порту 8080
- Маршрутизировать по префиксу URL
- Проверять API-ключ
- Ограничивать 100 запросов в минуту на IP
- Балансировать между двумя бэкендами
Шаг 1: Базовая структура
#include <boost/beast/core.hpp>
#include <boost/beast/http.hpp>
#include <boost/beast/version.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <unordered_map>
#include <memory>
#include <string>
namespace beast = boost::beast;
namespace http = beast::http;
namespace net = boost::asio;
using tcp = net::ip::tcp;
// Структура для хранения информации о бэкенде
struct Backend {
std::string host;
std::string port;
int weight;
};
// Класс маршрутизатора
class Router {
std::unordered_map<std::string, std::vector<Backend>> routes_;
std::unordered_map<std::string, size_t> counters_; // round-robin
public:
void add_route(const std::string& prefix, std::vector<Backend> backends) {
routes_[prefix] = std::move(backends);
counters_[prefix] = 0;
}
Backend get_backend(const std::string& path) {
for (const auto& [prefix, backends] : routes_) {
if (path.find(prefix) == 0) {
// Round-robin балансировка
size_t idx = counters_[prefix]++ % backends.size();
return backends[idx];
}
}
throw std::runtime_error("No route found");
}
};
Шаг 2: Middleware для rate limiting
Реализуем простой скользящий счетчик:
#include <chrono>
#include <map>
class RateLimiter {
std::map<std::string, std::pair<int, std::chrono::steady_clock::time_point>> clients_;
int max_requests_;
std::chrono::seconds window_;
public:
RateLimiter(int max_reqs, std::chrono::seconds win)
: max_requests_(max_reqs), window_(win) {}
bool allow(const std::string& ip) {
auto now = std::chrono::steady_clock::now();
auto& [count, reset_time] = clients_[ip];
if (now > reset_time + window_) {
count = 0;
reset_time = now;
}
if (count >= max_requests_) return false;
++count;
return true;
}
};
Шаг 3: Обработчик запросов
Сердце нашего API Gateway на C++ — функция, которая принимает запрос, прогоняет через middleware и отправляет на бэкенд:
template<class Stream>
void handle_request(Stream& stream, http::request<http::string_body>& req,
Router& router, RateLimiter& limiter) {
// 1. Rate limiting
auto ip = stream.remote_endpoint().address().to_string();
if (!limiter.allow(ip)) {
http::response<http::string_body> res{http::status::too_many_requests, req.version()};
res.body() = R"({"error":"rate_limit_exceeded"})";
res.prepare_payload();
http::write(stream, res);
return;
}
// 2. Аутентификация (упрощенно)
auto api_key = req[http::field::authorization];
if (api_key.empty() || api_key != "Bearer my-secret-key") {
http::response<http::string_body> res{http::status::unauthorized, req.version()};
res.body() = R"({"error":"invalid_api_key"})";
res.prepare_payload();
http::write(stream, res);
return;
}
// 3. Маршрутизация
Backend backend;
try {
backend = router.get_backend(req.target().to_string());
} catch (...) {
http::response<http::string_body> res{http::status::not_found, req.version()};
res.body() = R"({"error":"route_not_found"})";
res.prepare_payload();
http::write(stream, res);
return;