Rate Limiting в C# (.NET): полное руководство по библиотеке System.Threading.RateLimiting
В современных веб-приложениях и микросервисах критически важно контролировать нагрузку на ресурсы. Rate Limiting (ограничение скорости запросов) — это механизм, который предотвращает чрезмерное использование API, баз данных или других компонентов. В экосистеме .NET появилась встроенная библиотека System.Threading.RateLimiting, которая предоставляет гибкие и производительные инструменты для реализации rate limiting. В этой статье мы подробно разберём её возможности, архитектуру и примеры использования.
Что такое System.Threading.RateLimiting?
System.Threading.RateLimiting — это библиотека, входящая в состав .NET 7 и выше. Она предоставляет абстракции и реализации для различных стратегий ограничения скорости. Основной класс — RateLimiter, который управляет доступом к ресурсу, разрешая или отклоняя запросы на основе заданных правил.
Основные концепции
- RateLimiter — базовый класс, от которого наследуются все реализации.
- Acquire / TryAcquire — синхронные методы для получения разрешения.
- AcquireAsync / WaitAsync — асинхронные методы, позволяющие ожидать освобождения лимита.
- Metadata — дополнительная информация о лимите (например, время до сброса).
Встроенные стратегии Rate Limiting
Библиотека поддерживает четыре основные стратегии:
1. Фиксированное окно (Fixed Window)
Запросы группируются по временным окнам фиксированной длины. Если лимит превышен, запросы отклоняются до начала следующего окна.
using System.Threading.RateLimiting;
var options = new FixedWindowRateLimiterOptions
{
PermitLimit = 10,
Window = TimeSpan.FromSeconds(1),
QueueProcessingOrder = QueueProcessingOrder.OldestFirst,
QueueLimit = 0
};
var limiter = new FixedWindowRateLimiter(options);
for (int i = 0; i < 15; i++)
{
using var lease = await limiter.AcquireAsync();
if (lease.IsAcquired)
{
Console.WriteLine($"Запрос {i} выполнен");
}
else
{
Console.WriteLine($"Запрос {i} отклонён");
}
}
2. Скользящее окно (Sliding Window)
Аналогично фиксированному, но окно «скользит» с каждым запросом, обеспечивая более плавное ограничение. Разбивается на сегменты (segment).
var slidingOptions = new SlidingWindowRateLimiterOptions
{
PermitLimit = 10,
Window = TimeSpan.FromSeconds(1),
SegmentsPerWindow = 4,
QueueLimit = 0
};
var slidingLimiter = new SlidingWindowRateLimiter(slidingOptions);
3. Токен бакет (Token Bucket)
Один из самых популярных алгоритмов. Бакет вмещает определённое количество «токенов». Токены добавляются с заданной скоростью. Запрос потребляет один или несколько токенов. Если бакет пуст — запрос ждёт или отклоняется.
var tokenOptions = new TokenBucketRateLimiterOptions
{
TokenLimit = 100,
QueueLimit = 0,
ReplenishmentPeriod = TimeSpan.FromSeconds(1),
TokensPerPeriod = 10,
AutoReplenishment = true
};
var tokenLimiter = new TokenBucketRateLimiter(tokenOptions);
4. Конвейер (Concurrency Limiter)
Ограничивает количество одновременно выполняемых запросов. Полезно для управления параллелизмом, а не скоростью.
var concurrencyOptions = new ConcurrencyLimiterOptions
{
PermitLimit = 5,
QueueLimit = 10
};
var concurrencyLimiter = new ConcurrencyLimiter(concurrencyOptions);
Использование RateLimiter в ASP.NET Core
Начиная с .NET 7, rate limiting можно легко интегрировать в middleware ASP.NET Core. Регистрация сервисов и настройка политик:
// Program.cs
builder.Services.AddRateLimiter(options =>
{
options.AddFixedWindowLimiter("Fixed", config =>
{
config.PermitLimit = 10;
config.Window = TimeSpan.FromSeconds(1);
config.QueueLimit = 0;
});
options.RejectionStatusCode = StatusCodes.Status429TooManyRequests;
});
app.UseRateLimiter();
app.MapGet("/api/resource", async context =>
{
await context.Response.WriteAsync("Hello from limited API!");
}).RequireRateLimiting("Fixed");
Продвинутые возможности
- Кастомные политики — можно реализовать собственный
RateLimiter, унаследовавшись от базового класса. - Партиционирование — создание отдельных лимитов для разных клиентов (IP, API Key