Rate Limiting в C++: библиотеки и реализация для защиты API

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

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

Начать курс

Rate Limiting в C++: обзор инструментов и реализация

Rate limiting (ограничение скорости запросов) — критически важный механизм для защиты веб-сервисов, API и микросервисов от перегрузок, DDoS-атак и нерационального использования ресурсов. В экосистеме C++ существует несколько зрелых библиотек и подходов для реализации rate limiting. В этой статье мы рассмотрим основные алгоритмы, популярные библиотеки и напишем примеры кода.

Популярные алгоритмы rate limiting

Прежде чем выбирать библиотеку, полезно понимать, какие алгоритмы лежат в основе:

  • Token Bucket (Корзина токенов) — токены добавляются с фиксированной скоростью, запрос потребляет один токен. Если токенов нет — запрос отклоняется. Прост и эффективен.
  • Sliding Window (Скользящее окно) — учитывает количество запросов за последний временной интервал (например, 10 запросов за 1 секунду).
  • Fixed Window (Фиксированное окно) — простой счётчик запросов за фиксированный период (например, каждую минуту счётчик обнуляется).
  • Leaky Bucket (Дырявое ведро) — запросы ставятся в очередь и обрабатываются с постоянной скоростью, избыток отбрасывается.

Библиотеки для rate limiting в C++

1. Boost.Token Bucket (Boost.Timer + ручная реализация)

В Boost нет готовой реализации token bucket, но её легко написать поверх boost::timer или std::chrono. Однако для production-решений лучше использовать полноценные библиотеки.

2. libcppa (C++ Actor Framework)

Библиотека libcppa (теперь часть CAF — C++ Actor Framework) предоставляет встроенные механизмы rate limiting для акторов. Позволяет ограничивать количество сообщений в единицу времени.

#include <caf/all.hpp>

using namespace caf;

behavior rate_limited_actor(event_based_actor* self, int max_requests, duration period) {
    auto limiter = self->make_rate_limiter(max_requests, period);
    return {
        [=](const std::string& msg) {
            if (limiter->try_acquire()) {
                aout(self) << "Processed: " << msg << endl;
            } else {
                aout(self) << "Rate limit exceeded for: " << msg << endl;
            }
        }
    };
}

3. userver (от Яндекса)

Фреймворк userver от Яндекса — мощное решение для высоконагруженных сервисов на C++. Включает встроенную поддержку rate limiting через components::RateLimiter и utils::TokenBucket.

#include <userver/utils/token_bucket.hpp>

using namespace userver;

int main() {
    utils::TokenBucket limiter(10, std::chrono::seconds(1)); // 10 запросов в секунду
    for (int i = 0; i < 20; ++i) {
        if (limiter.TryObtain()) {
            std::cout << "Request " << i << " allowed\n";
        } else {
            std::cout << "Request " << i << " blocked\n";
        }
        std::this_thread::sleep_for(std::chrono::milliseconds(50));
    }
    return 0;
}

4. Pistache (HTTP-фреймворк с rate limiting)

Библиотека Pistache для создания HTTP-серверов имеет встроенный middleware для rate limiting. Пример:

#include <pistache/endpoint.h>
#include <pistache/router.h>
#include <pistache/http.h>

using namespace Pistache;

int main() {
    Http::Endpoint server(Address(Ipv4::any(), Port(9080)));
    auto router = std::make_shared<Rest::Router>();
    
    // Встроенный rate limiter: 5 запросов в секунду
    router->addMiddleware(Http::Middleware::RateLimit(5, std::chrono::seconds(1)));
    
    Rest::Routes::Get(*router, "/api/resource", [](const Rest::Request& req, Http::ResponseWriter response) {
        response.send(Http::Code::Ok, "Hello, world!");
    });
    
    server.init();
    server.setHandler(router->handler());
    server.serve();
}

Реализация Token Bucket с нуля

Если вы не хотите использовать внешние библиотеки, можно написать простой thread-safe token bucket на C++11/14:

#include <chrono>
#include <mutex>
#include <atomic>

class TokenBucket {
public:
    TokenBucket(size_t max_tokens, std::chrono::microseconds interval)
        : max_tokens_(max_tokens), interval_(interval), tokens_(max_tokens), last_ref                    

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