Как реализовать Rate Limiter и какие алгоритмы используются?

Ответ

Rate Limiter — это механизм, предназначенный для контроля частоты запросов к системе, предотвращая её перегрузку и защищая от злоупотреблений (например, DDoS-атак).

Пример реализации на Python с использованием алгоритма Token Bucket:

import time

class RateLimiter:
    def __init__(self, capacity: int, refill_rate: float):
        """
        Инициализация Rate Limiter.
        :param capacity: Максимальное количество токенов (запросов).
        :param refill_rate: Скорость пополнения токенов в секунду.
        """
        self.capacity = capacity
        self.tokens = capacity
        self.last_refill = time.time()
        self.refill_rate = refill_rate  # tokens per second

    def allow_request(self) -> bool:
        """
        Проверяет, разрешен ли запрос.
        :return: True, если запрос разрешен, False иначе.
        """
        now = time.time()
        elapsed = now - self.last_refill
        # Пополняем токены, но не превышаем capacity
        self.tokens = min(self.capacity, self.tokens + elapsed * self.refill_rate)
        self.last_refill = now

        if self.tokens >= 1:
            self.tokens -= 1
            return True
        return False

# Пример использования:
# limiter = RateLimiter(capacity=5, refill_rate=1) # 5 запросов, 1 токен/сек
# for _ in range(10):
#     if limiter.allow_request():
#         print("Request allowed")
#     else:
#         print("Request denied")
#     time.sleep(0.5)

Сферы применения:

  • API-сервисы (REST/gRPC): Защита от перегрузок, злоупотреблений и обеспечение справедливого использования ресурсов.
  • Ограничение вызовов внешних API: Контроль над количеством запросов к сторонним сервисам (например, Telegram Bot API, платежные шлюзы) для соблюдения их лимитов.

Другие алгоритмы и готовые решения:

  • Фиксированное окно (Fixed Window): Разрешает N запросов в течение фиксированного временного интервала. Прост в реализации, но может быть уязвим к всплескам запросов в начале или конце окна.
  • Скользящее окно (Sliding Window): Более сложный, но точный алгоритм, который учитывает запросы из предыдущего окна, обеспечивая более плавное ограничение.
  • Готовые решения: Для распределённых систем часто используются Redis с модулями типа redis-cell или специализированные библиотеки, такие как FastAPI-Limiter для Python-фреймворков.

Важно: В распределённых системах необходимо учитывать race conditions, поскольку одновременные запросы могут привести к некорректному состоянию лимитера. В таких случаях рекомендуется использовать распределённые хранилища (например, Redis) с атомарными операциями для обеспечения консистентности.

Ответ 18+ 🔞

А, слушай-ка, про Rate Limiter, да? Это ж та самая штука, которая, блядь, как вышибала в переполненном баре стоит. Подходит к тебе и говорит: «Ну-ка, полегче, дружок, ты у нас уже пятую кружку за минуту требуешь, давай-ка потише».

Вот смотри, как эта бандура в коде выглядит, на примере «Ведра с токенами»:

import time

class RateLimiter:
    def __init__(self, capacity: int, refill_rate: float):
        """
        Инициализация Rate Limiter.
        :param capacity: Максимальное количество токенов (запросов).
        :param refill_rate: Скорость пополнения токенов в секунду.
        """
        self.capacity = capacity
        self.tokens = capacity
        self.last_refill = time.time()
        self.refill_rate = refill_rate  # tokens per second

    def allow_request(self) -> bool:
        """
        Проверяет, разрешен ли запрос.
        :return: True, если запрос разрешен, False иначе.
        """
        now = time.time()
        elapsed = now - self.last_refill
        # Пополняем токены, но не превышаем capacity
        self.tokens = min(self.capacity, self.tokens + elapsed * self.refill_rate)
        self.last_refill = now

        if self.tokens >= 1:
            self.tokens -= 1
            return True
        return False

Представь: у тебя есть ведро. В нём, допустим, 5 токенов-шариков. Каждый запрос — вытащил один шарик. А сверху капает водичка и новые шарики подкидывает, скажем, 1 шарик в секунду. Если шарики кончились — всё, братан, сиди и жди, пока накапает. Запросы упёрлись в пизду. Вот и вся магия, ёпта.

А где эту хрень применяют? Да везде, блядь!

  • Твои APIшки: Чтобы какой-нибудь умник не начал слать тебе запросы со скоростью «хуй с горы» и не положил сервер, пока ты кофе пьёшь. Или чтобы все клиенты по-честному, а не кто первый встал — того и тапки.
  • Когда ты сам лезешь в чужие API: Вот, например, пишешь бота для Телеграма. А они там, суки, лимиты ставят — не больше 30 сообщений в секунду. Вот ты и ставишь такого вышибалу перед своими запросами, чтобы не получить банхаммером по ебалу за «Too Many Requests».

Другие алгоритмы, там, для гурманов:

  • «Фиксированное окно»: Всё просто, блядь. В течение минуты — не больше 100 запросов. Раз — и всё. Но есть подвох: если в последнюю секунду минуты набить 100 запросов, а в первую секунду следующей — ещё 100, то система, блядь, за секунду получит 200 запросов и накроется медным тазом. Не очень хитро.
  • «Скользящее окно»: Это уже поумнее. Оно смотрит не на календарную минуту, а на последние 60 секунд в любой момент времени. Тут уже не проскочишь, хитрая жопа. Но и код сложнее, конечно.
  • Готовые решения: Зачем, блядь, велосипед изобретать? Для распределённых систем бери Redis с каким-нибудь redis-cell. Или, если на FastAPI сидишь, есть FastAPI-Limiter — прикрутил и забыл.

И главное, на что смотреть, ёпта: Если у тебя не один сервер, а целая орава, то race conditions, блядь, обеспечены. Два запроса в одно и то же время на разных машинах решат, что токен ещё есть, и оба пролезут. Для такого дела нужна одна точка правды — типа Redis, где операции атомарные. А то будет, как в том анекдоте: «А я думал, ты взял!».