System.Threading.RateLimiting для .NET
Хотя ограничение пропускной способности является хорошо известной проблемой для веб-серверов, существует множество других ситуаций, когда требуются аналогичные возможности. Например, клиентское приложение может выиграть, если будет иметь собственное встроенное ограничение, чтобы оно не заставляло срабатывать ограничения веб-сервера. И для других ресурсов, таких как файловые серверы и серверы баз данных, могут потребоваться ограничения пропускной способности, чтобы клиенты не могли монопольно использовать их.
Хотя теоретически разработчики могут создавать свои собственный функционал ограничений, используя семафоры, такой код может быть трудным и подвержен ошибкам. .NET предложит новый пакет под названием System.Threading.RateLimiting. Этот пакет будет определять общую структуру ограничений пропускной способности и предоставлять несколько вариантов ограничений из коробки.
Абстрактный базовый класс, который должны реализовать все ограничители, просто называется
RateLimiter. Он предлагает как синхронный, так и асинхронный способ получения слота (lease), а также подсчёт доступных слотов. Слот - это разрешение ограничителя на выполнение работы. Он представлен классом RateLimitLease, который должен быть возвращён в пул после завершения текущей задачи.В настоящее время предлагаются четыре встроенных ограничителя. Самым простым является
ConcurrencyLimiter, который, как семафор, позволяет одновременно использовать только определённое количество параллельных рабочих процессов.FixedWindowRateLimiter и SlidingWindowRateLimiter ограничивают количество запросов в течение заданного временного окна. Первый использует простой временной интервал для сброса счетчика. Второй предлагает более сложный вариант, когда временное окно разделено на N сегментов со счётчиком запросов для каждого сегмента.TokenBucketRateLimiter основан на алгоритме token bucket. Обычно рабочий процесс приобретает один слот, но бывают сценарии, когда их требуется несколько одновременно. Например, каждый слот может представлять собой разрешение на использование ядра ЦП или блока памяти. В этом случае TokenBucketRateLimiter может быть более полезным. Он может организовать запросы на основе количества слотов, которые требует каждый рабочий процесс, и доступных слотов в каждом ограниченном по времени сегменте.Если ограничитель не может удовлетворить запрос на слот, его поведение меняется. Для синхронного случая возвращается
RateLimitLease с флагом, указывающим на сбой. В асинхронном случае запросы могут быть поставлены в очередь. Если очередь превышает настраиваемый предел, рабочие процессы могут быть отвергнуты. Программисты могут настроить, будет ли отвергнут самый старый или самый новый запрос в этом сценарии.Один из вариантов использования, который эта библиотека явно не поддерживает, - это агрегированные ограничители. Объясняется это тем, что для поддержки сценариев, в которых требуется агрегированный ограничитель, требуется отдельная абстракция. К примеру, чтобы реализовать ограничение скорости по IP, ограничителю придётся реализовать отдельное ограничение для каждого сегмента, то есть принимать идентификатор сегмента (IP адрес). Здесь же реализованы более простые ограничители, где идентификатор не требуется. Пока каких-либо вариантов использования агрегированных ограничителей в dotnet/runtime не нашли, поэтому их решили не добавлять в текущую реализацию.
Если вам нужен агрегированный ограничитель, можете рассмотреть, например, пакеты, разработанные специально для ASP.NET Core, такие как AspNetCoreRateLimit.
Источник: https://www.infoq.com/news/2021/08/DotNet-Rate-Limiting/