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