Rate Limiter для API социальной сети с нагрузкой до 1 млн запросов в секунду — уже не «фильтр перед бэкендом», а отдельная инфраструктурная система со своими компромиссами. В свежем разборе задача выглядит знакомо: ограничить запросы по пользователю, IP или API-ключу, не утонуть в задержках и не развалиться при росте трафика.
Как пишет Habr / Карьера, в центре такого дизайна оказываются не только сами лимиты, но и выбор алгоритма, точка интеграции в архитектуру и способ хранения состояния. Для русскоязычных разработчиков и архитекторов это полезный разбор без романтики: rate limiting здесь показан не как галочка в API Gateway, а как система, которая начинает болеть ровно в тот момент, когда продукт становится по-настоящему массовым.
Исходная постановка вполне прикладная. Нужно ограничивать HTTP-запросы на сервере, а не полагаться на клиентскую дисциплину, потому что клиент можно взломать, переписать или просто проигнорировать. Система должна различать клиентов по user ID, IP-адресу или ключу API, применять настраиваемые правила и при превышении лимита возвращать HTTP 429 с понятными заголовками вроде остатка запросов и времени сброса окна. Интерфейс у этого сервиса почти нарочито простой: проверка по clientId и ruleId должна вернуть, можно ли пропустить запрос, сколько попыток осталось и когда лимит обновится.
Дальше начинается то, на чем обычно ломаются слишком красивые схемы. Самое очевидное решение — считать запросы прямо внутри каждого приложения, в памяти процесса. На бумаге это быстро: без сетевых вызовов, без Redis, без дополнительной инфраструктуры. На практике такой Rate Limiter видит только локальный трафик конкретного инстанса. Если у вас пять серверов, лимит «100 запросов в минуту» легко превращается в «примерно 500, если повезет с балансировщиком». Для распределенной системы это не ограничитель, а скорее пожелание в сторону клиента.
Поэтому логичная точка размещения — ближе к входу в систему, через API Gateway или отдельный rate limiting service, который видит весь поток запросов. Такой подход позволяет централизованно применять правила до того, как трафик дойдет до внутренних сервисов и начнет тратить CPU, память и соединения с БД. Это особенно важно для систем, где всплеск нагрузки сам по себе уже инцидент, даже если половина запросов потом вернется с ошибкой на уровне бизнес-логики. В этой архитектуре gateway становится местом, где ограничения выполняются последовательно, а прикладные сервисы могут не дублировать одну и ту же защитную механику.
Почему в таких задачах всплывает Token Bucket
Отдельный слой решения — выбор алгоритма. В разборе перечисляется стандартный набор кандидатов: fixed window, sliding window, leaky bucket, token bucket. Но при высокой нагрузке и требованиях к практичности часто выигрывает именно Token Bucket. Причина не в том, что он «лучший вообще», а в балансе. Он позволяет переживать краткие всплески трафика, если у клиента накоплены токены, и при этом не превращает реализацию в тяжелую математику на каждый запрос. Для пользовательских API это полезно: человек или сервис может сделать короткий burst, не утыкаясь мгновенно в стену, но в среднем скорость все равно остается под контролем.
У fixed window есть известная проблема на границах окна, когда клиент может выстрелить пакет запросов в последние секунды одного интервала и сразу повторить то же самое в начале следующего. Sliding window точнее, но дороже по состоянию и вычислениям. Leaky bucket хорош там, где важен ровный отток, но хуже подходит для сценариев, где небольшие всплески допустимы. Token Bucket в этом смысле выглядит не академически идеальным, а инженерно удобным. Это и объясняет, почему его так часто предлагают на system design-интервью и так же часто используют в реальных API-платформах.
Следующий практический вопрос — где хранить состояние. Если лимит должен работать глобально, локальная память не подходит. В статье в качестве естественного варианта рассматривается Redis: быстрый in-memory storage, который умеет атомарные операции, а значит, подходит для счетчиков и обновления лимитов без лишней гонки состояний. На каждую комбинацию клиента и правила можно хранить ключ с текущим числом токенов, временем последнего обновления или счетчиком в окне. Такой подход сохраняет низкую задержку и при этом не заставляет каждый application server жить в своей отдельной версии реальности.
Но Redis не волшебная коробка, после которой можно расходиться. Один инстанс быстро становится узким местом, если речь идет не о «тысячах запросов в день», а о 1 млн запросов в секунду и 100 млн активных пользователей ежедневно. Здесь в статье важна сама постановка нефункциональных требований: автор прямо показывает, что масштаб нельзя оставлять за скобками. От ответа на вопрос «стартап у нас или большая платформа» зависит почти все — от выбранного алгоритма до топологии хранения и шардирования. Если лимитер строится под такой поток, его придется горизонтально масштабировать, распределять ключи между несколькими узлами, мириться с конечной согласованностью и заранее принять, что строгая глобальная консистентность на каждом запросе будет слишком дорогой.
Что это значит для команд, которые строят API
Для разработчиков здесь главный вывод довольно приземленный: Rate Limiter нельзя считать косметической функцией. Это часть надежности API, защиты от злоупотреблений и механики справедливого доступа к ресурсам. Причем проектировать ее лучше не после первой атаки или первого скандала с «шумным соседом», а до того, как продукт начнет масштабироваться. Если лимиты живут кусками в разных сервисах, правила размазаны по конфигам, а заголовки ответа неинформативны, поддержка превращается в угадайку, а клиенты начинают лечить симптомы ретраями и хаотичными backoff-стратегиями.
Для бизнеса и продуктовых команд полезен другой слой смысла. Ограничение запросов — это не только про безопасность, но и про экономику. Оно помогает удерживать инфраструктурные расходы, предсказуемо разделять ресурсы между сегментами пользователей, делать разные тарифы и не позволять одному клиенту случайно или намеренно сжечь общую емкость. В B2B API это почти всегда упирается в коммерческие правила, а в потребительских продуктах — в стабильность ключевых сценариев. Когда лимитер спроектирован плохо, платит не только SRE-команда, но и весь продукт: через задержки, отказы и странные деградации, которые пользователь описывает одной фразой — «сервис опять тормозит».
Интересно, что жанр system design здесь работает лучше многих «боевых» кейсов. Он не продает готовый рецепт, а показывает, где именно прячутся компромиссы: точность против стоимости, локальная скорость против глобальной видимости, строгая согласованность против доступности. И чем больше компаний строят API как основу продукта, тем быстрее выясняется неприятная правда: хороший Rate Limiter — это не middleware на сдачу, а одна из тех систем, которые никто не замечает, пока она работает, и сразу замечают, когда она была спроектирована на авось.
На этом фоне главный вопрос уже не в том, нужен ли отдельный ограничитель запросов, а в том, когда команда перестанет считать его второстепенной задачей. При нагрузках уровня миллионов запросов в секунду выигрывают не те, кто первым поставил Redis и выбрал Token Bucket, а те, кто заранее понял: лимиты — это часть архитектуры продукта, а не заплатка перед следующим всплеском трафика.