BroadNet
HTTP API & webhooksНадёжность

Идемпотентность и ограничение скорости: ключи, 429, Retry-After, RateLimit

Обновлено 5 мин чтенияна основе стандартов
Прямой ответ

Заголовок Idempotency-Key (IETF-черновик) делает повторённый POST безопасным: сервер хранит первый результат по ключу в пределах окна дедупликации и воспроизводит его для любого повтора, давая отправку exactly-once. Ограничение скорости защищает сервис: сверхлимитные запросы получают 429 Too Many Requests с Retry-After, а заголовки RateLimit объявляют остаток квоты, чтобы клиенты сами себя ограничивали.

§01

Заголовок Idempotency-Key

Отправка сообщения — это POST, который не идемпотентен, однако сети вынуждают клиентов повторять, когда ответ потерян, — создавая классическую двойную отправку. Черновик Idempotency-Key рабочей группы IETF по HTTP-API стандартизирует решение: клиент генерирует уникальный ключ (UUID) на каждую логическую операцию и отправляет его в заголовке запроса Idempotency-Key. Сервер записывает ключ с исходом первого запроса, и если видит тот же ключ снова, он не выполняет второй раз — он возвращает сохранённый результат. Так повтор после тайм-аута гарантированно безвреден: либо первая попытка не долетела (и эта выполняется), либо долетела (и её результат воспроизводится). Ключ принадлежит операции, а не соединению, поэтому клиент переиспользует один и тот же ключ во всех повторах этой одной отправки.

Идемпотентный POST с ключом, сгенерированным клиентомhttp
# Generic IETF-draft example (not the BroadNet API, which takes no
# Idempotency-Key header: see /developers/idempotency)
POST /v1/messages HTTP/1.1
Host: api.example
Idempotency-Key: 8f14e45f-ea7b-4c0f-9b2a-1d2c3e4f5a6b
Content-Type: application/json

{ "to": "+15551234567", "text": "..." }

# first call  -> 202 Accepted, id 01HZY8Q3M6   (executed, stored)
# same key    -> 202 Accepted, id 01HZY8Q3M6   (replayed, not re-sent)
§02

Окно дедупликации, воспроизведение ответа и конфликты

Ключ идемпотентности не помнится вечно; сервер держит его в течение окна дедупликации (обычно 24 часа), достаточного, чтобы покрыть любой реалистичный повтор, а затем удаляет его. В пределах этого окна повтор ключа воспроизводит сохранённый код состояния и тело дословно — тот же 202 и тот же message id, — поэтому клиент видит ровно то, что увидел бы, если бы первый ответ пришёл. Два краевых случая требуют явной обработки. Если ключ переиспользуется с другим телом запроса, это ошибка клиента, и сервер возвращает 409 Conflict, а не молча отправляет не то. Если повтор приходит, пока первый запрос ещё в полёте, сервер возвращает конфликт или «в процессе», а не начинает второе выполнение, чтобы конкурентность не могла победить гарантию.

Исходы Idempotency-Key
СитуацияПоведение сервераСтатус
Новый ключВыполнить, сохранить ключ + результатНормальный (напр. 202)
Повтор ключа, то же телоВернуть сохранённый ответ, не выполнять повторноИсходный статус
Повтор ключа, другое телоОтклонить — ключ привязан к первой полезной нагрузке409 Conflict
Повтор, пока первый запрос ещё обрабатываетсяНе запускайте второе выполнение409 / в процессе
Срок действия ключа истёк (за пределами окна)Обрабатывается как новоеНормальный
§03

Ограничение скорости и 429

Разделяемая платформа сообщений защищает себя и других арендаторов, ограничивая, как быстро любой клиент может вызывать, и сигнализирует о нарушении конкретным статусом. RFC 9110 и RFC 6585 определяют 429 Too Many Requests именно для этого: клиент отправил слишком много запросов за данное время. Дисциплинированный сервер сопровождает 429 заголовком Retry-After, сообщающим клиенту, сколько ждать перед новой попыткой (либо задержку в секундах, либо HTTP-дату). Обязанность клиента — соблюсти его: остановиться, подождать указанный интервал, затем возобновить, — а не повторять немедленно, что лишь усугубляет перегрузку. 429 временный и безопасен для повтора, в отличие от 4xx, указывающего на некорректный запрос.

429 с заголовками Retry-After и RateLimithttp
HTTP/1.1 429 Too Many Requests
Retry-After: 2
RateLimit: limit=100, remaining=0, reset=2
RateLimit-Policy: 100;w=1
Content-Type: application/problem+json

{ "type": "https://example/problems/rate-limited",
  "title": "Too Many Requests", "status": 429 }
§04

Заголовки RateLimit и RateLimit-Policy

Реагировать на 429 постфактум расточительно; черновик IETF по заголовкам RateLimit позволяет клиенту увидеть свой бюджет прежде, чем он его превысит. На обычных ответах сервер объявляет текущую квоту заголовком RateLimit, несущим лимит, оставшийся резерв и секунды до сброса окна, и заголовком RateLimit-Policy, описывающим саму политику (например, 100 запросов на окно в 1 секунду). Клиент, читающий их, может задавать себе темп — замедляясь по мере приближения remaining к нулю — и вовсе не вызывать 429. Это превращает ограничение скорости из карательного отказа постфактум в кооперативный сигнал, что и есть весь смысл стандартизации имён заголовков, чтобы любая клиентская библиотека могла их потреблять.

Стандартные заголовки сигнализации ограничения скорости
ЗаголовокПередаетПример
429 Too Many RequestsСтатус превышения лимитастрока состояния
Retry-AfterСколько времени нужно подождать, прежде чем повторить попыткуRetry-After: 2
RateLimitлимит / оставшееся / сброс для окнаlimit=100, remaining=17, reset=1
RateLimit-PolicyДействующая политика квот100;w=1 (100 per 1s)
§05

Token bucket и как навязываются лимиты

Алгоритм token bucket — обычный механизм за этими заголовками, поскольку он ограничивает устойчивую скорость, допуская короткие всплески. Ведро держит до фиксированного числа токенов и пополняется с постоянной скоростью; каждый запрос забирает один токен, а запрос, нашедший ведро пустым, отклоняется с 429. Размер ведра задаёт всплеск, который клиент может потратить сразу, а скорость пополнения задаёт долгосрочный потолок, так что клиент может отправить быстрый всплеск после простоя, но не может превысить среднее. Значение remaining в RateLimit фактически является счётчиком токенов, а reset — момент, когда ведро пополнится, — что и есть та информация, которая нужна клиенту с заданным темпом. Этот контроль потока на уровне HTTP — REST-аналог размера окна SMPP и ограничения enquire_link на бинарной сессии.

§06

Обратное давление клиента

Клиентская половина хорошего поведения при ограничении скорости — обратное давление: формирование исходящей нагрузки так, чтобы она оставалась в пределах объявленного бюджета, вместо того чтобы выстрелить всё и поглощать 429. На практике это означает клиентский лимитер (token bucket, зеркалящий серверный), чтение заголовков RateLimit для корректировки темпа и, при 429, выдержку на интервал Retry-After с джиттером перед возобновлением. Для массовой отправки это означает дозирование подач под устойчивый потолок, а не их сброс разом, чтобы платформа оставалась здоровой, а собственная задержка клиента — предсказуемой. BroadNet документирует своё конкретное поведение идемпотентности и ограничения скорости для разработчиков.

Часто задаваемые вопросы

Часто задаваемые вопросы

Что делает заголовок Idempotency-Key?

Он делает повторённый POST безопасным. Клиент отправляет уникальный ключ на каждую логическую операцию; сервер выполняет первый запрос с этим ключом и сохраняет результат, затем воспроизводит сохранённый результат для любого повтора того же ключа в пределах окна дедупликации. Так повтор после потерянного ответа либо выполняется один раз, либо воспроизводится — никогда не отправляет дважды. Он специфицирован в черновике рабочей группы IETF по HTTP-API.

Как долго сервер помнит ключ идемпотентности?

В течение окна дедупликации — обычно около 24 часов — достаточно долго, чтобы покрыть любой реалистичный повтор, после чего ключ истекает, и переиспользование трактуется как новое. В пределах окна повтор ключа воспроизводит исходный код состояния и тело дословно, а не выполняет заново.

Что произойдёт, если переиспользовать ключ идемпотентности с другим телом?

Сервер отклонит его с 409 Conflict. Ключ привязан к полезной нагрузке первого запроса, поэтому его переиспользование с другим телом трактуется как ошибка клиента, а не молчаливая отправка не того сообщения. Переиспользуйте тот же ключ только для повторов идентичной операции.

Что должен делать клиент при получении 429?

Остановиться и соблюсти заголовок Retry-After — подождать указанные секунды (или до заданной даты), затем возобновить, — а не повторять немедленно, что лишь усугубляет перегрузку. 429 временный и безопасен для повтора после истечения ожидания, в идеале с небольшим джиттером, чтобы многие клиенты не возобновились в унисон.

Для чего нужны заголовки RateLimit и RateLimit-Policy?

Они позволяют клиенту увидеть свою квоту прежде, чем превысить её. RateLimit несёт лимит, оставшийся резерв и время сброса для текущего окна; RateLimit-Policy описывает политику (например, 100 запросов в секунду). Клиент, читающий их, может задавать себе темп и вовсе не вызывать 429. Оба определены в черновике IETF.

Как token bucket соотносится с заголовками RateLimit?

Token bucket — это механизм навязывания: ведро фиксированного размера пополняется с постоянной скоростью, каждый запрос тратит токен, а пустое ведро даёт 429. Значение remaining в RateLimit по сути является текущим счётчиком токенов, а reset — момент пополнения ведра, — так что клиент, читающий эти заголовки, точно знает, сколько всплеска и устойчивой скорости у него осталось.

Ссылки

Технические характеристики источника

На этой странице представлены опубликованные стандарты, на которых они основаны. (Открывается в новой вкладке)

BNПоддерживается инженерной командой BroadNet·Обмен сообщениями операторского класса с 2003 года.
Прикрепите это к проводу

Разработано с использованием реального SMPP подключения и REST API.

Запросите тестовый доступ с реальными тестовыми ключами: сначала отправьте их на свой подтвержденный номер, а затем, после утверждения вашей личности отправителя и маршрутов, переходите к использованию в производственной среде более чем в 200 странах.