Заголовок Retry-After: как долго ждать, прежде чем повторить попытку

Retry-After — это заголовок HTTP-ответа, который сообщает приложению, как долго ждать, прежде чем приложение отправит следующий запрос. Значение представляет собой либо число секунд, либо дату HTTP. Когда API отправляет его, это самый надежный ответ на вопрос «когда я могу повторить попытку?», так как он поступает с сервера, который отклонил ваш запрос. Дополнительные сведения см. в разделе RFC 9110, раздел 10.2.3.

Как выглядит Retry-After

Заголовок имеет 2 формата. Приложение должно обрабатывать и то и другое.

Format Example Что это означает
Seconds Retry-After: 120 Подождите 120 секунд (2 минуты) с момента получения ответа. Значение — это не отрицательное целое число.
HTTP-дата Retry-After: Fri, 31 Dec 1999 23:59:59 GMT Не отправляйте запрос еще раз до этого времени. Дата всегда указана в GMT.

Серверы отправляют Retry-After с этими кодами состояния:

Status Что Retry-After означает Source
429 Too Many Requests Как долго ждать перед тем, как отправить новый запрос. Сервер может содержать его. RFC 6585, раздел 4
503 Service Unavailable Как долго служба, как ожидается, будет недоступна. Сервер может содержать это. RFC 9110, раздел 15.6.4
413 Content Too Large Если это состояние является временным, сервер должен указать, через какое время оно закончится. RFC 9110, раздел 15.5.14
Любое 3xx перенаправление Минимальное время, которое нужно выждать, прежде чем перейти по перенаправлению. RFC 9110, раздел 10.2.3

Заголовок необязателен. Вместо этого некоторые API используют собственные заголовки. Например, GitHub сообщает, когда ваш лимит сбрасываетсяx-ratelimit-reset. Дополнительные сведения см. в разделе Превышен лимит запросов API GitHub.

Как обрабатывать Retry-After

  1. Прочитайте оба формата. Если значение — число, это секунды. В противном случае разберите его как дату и вычтите текущее время. Если дата уже в прошлом, вы можете повторить попытку сразу.
  2. Подождите по крайней мере столько времени, сколько указано в заголовке. Если повторить попытку раньше, это обычно приводит к другому 429 или 503. Некоторые API продолжают учитывать ваши запросы даже при ограничении их частоты, поэтому слишком ранние повторные попытки могут увеличить время ожидания. Например, см. рекомендации по ограничению скорости в Microsoft Graph.
  3. Перейдите на резервный вариант backoff с jitter, когда заголовок отсутствует. Удвойте ожидание после каждой неудачной попытки, добавьте случайную величину, чтобы многие клиенты не пытались повторно в один и тот же момент, и ограничьте максимальное время ожидания.
  4. Ограничьте повторные попытки. После нескольких попыток верните ошибку вызывающему коду.
  5. Убедитесь, что повторная попытка может помочь. Некоторые API возвращают 429, когда ваши кредиты или лимит расходов исчерпаны. Ожидание не исправит это. Пример см. в insufficient_quota OpenAI и credit_balance_exhausted.
function retryDelayMs(response, attempt) {
  const value = response.headers.get('retry-after');
  if (value) {
    const seconds = Number(value);
    const ms = Number.isNaN(seconds) ? Date.parse(value) - Date.now() : seconds * 1000;
    if (!Number.isNaN(ms)) {
      return Math.max(ms, 0);
    }
  }
  // No usable header: exponential backoff with jitter, capped at 30 seconds
  return Math.random() * Math.min(30_000, 1_000 * 2 ** attempt);
}

Многие SDK обрабатывают Retry-After за вас, но только до тех пор, пока у них не закончатся попытки повтора. Затем код получает ошибку.

SDK Что оно делает по умолчанию
.NET стандартный обработчик устойчивости Повторяет ответы 429, 5xx и 408 до 3 раз с экспоненциальной задержкой и джиттером. Он используется Retry-After для задержки, так как ShouldRetryAfterHeader по умолчанию используется true.
Пакеты SDK Microsoft Graph Используйте Retry-After, когда он присутствует, и используйте экспоненциальную задержку в противном случае. Запросы внутри пакета JSON не повторяются автоматически.
SDK OpenAI для Python Повторяет попытки при ошибках подключения и ответах 408, 409, 429 и 5xx 2 раза с короткой экспоненциальной задержкой. Установите max_retries, чтобы изменить это.

Проверьте документацию пакета SDK для уточнения точной политики и проверьте, что происходит после того, как последняя повторная попытка завершится неудачей.

Проверка того, что приложение обрабатывает Retry-After

Вы редко сталкиваетесь с Retry-After во время разработки, а если это происходит, вы не можете контролировать его значение. Таким образом, то, как вы это тестируете, определяет, найдёте ли вы ошибки раньше ваших пользователей.

Approach Что вы найдете Чего вам не хватает
Ожидание промышленной среды Реальные сбои Всё, пока пользователь не нажмёт на него
Создайте мок для API в тестах или позвольте агенту кодирования написать мок Разбирает ли ваш код заголовок Достаточно ли долго ждёт ваш реальный HTTP-клиент или SDK, и что API действительно отправляет. Вашему приложению также нужен переключатель, используемый только для тестирования, чтобы обращаться к мок-объекту.
Вызывайте реальный API до тех пор, пока он не начнет ограничивать частоту запросов Реальное поведение Вы не можете вызвать ответ с ограничением скорости по запросу, и вы используете реальную квоту.
Перехватывайте реальный трафик приложения и возвращайте ответы с ограничением скорости по запросу Ждут ли ваш фактический SDK и политика повторных попыток столько, сколько указано в заголовке HTTP Ничего в приложении не изменяется, поэтому код не проверяется в изоляции. Оставьте это для модульных тестов.

Попробуйте это в своем приложении

Dev Proxy перехватывает запросы приложения к выбранным API и возвращает 429 ответы с заголовком Retry-After, а приложение продолжает вызывать реальные URL-адреса. RetryAfterPlugin запоминает, когда можно повторить каждый запрос с ограничением скорости. Если приложение вызывает тот же URL-адрес до этого времени, служба Dev Proxy сообщает об этом и снова ограничивает частоту запросов. Подключаемый модуль отслеживает 429 только ответы.

В файле ошибок для GenericRandomErrorPlugin задайте Retry-After значение 429 ответа @dynamic, и Dev Proxy заполнит количество секунд и отследит его для вас.

Чтобы попробовать это, скачайте пресет, который использует оба плагина, и запустите с ним Dev Proxy:

devproxy config get openai-throttling
devproxy --config-file "~dataFolder/configs/openai-throttling/.devproxy/devproxyrc.json"

Затем запустите приложение как обычно и посмотрите, что оно делает. Чтобы установить Dev Proxy, см. статью "Настройка Dev Proxy".

Дальнейшие действия

См. также