Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Код состояния от 500 до 599 означает, что сервер не смог выполнить запрос, который выглядел допустимым. Проблема не в вашем запросе, поэтому повторная отправка может сработать. Следует ли отправлять его снова, зависит от кода состояния и от того, какое действие выполняет запрос. Определения см. в RFC 9110, разделе 15.6.
Что означает каждый код состояния
| Код состояния | Что это означает | Повторите попытку? |
|---|---|---|
500 Internal Server Error |
Сервер столкнулся с непредвиденным условием | Это зависит от API. Некоторые API, такие как API Claude, говорят вам повторить запрос при ошибке 500 с экспоненциальной задержкой между повторами. Проверьте документацию API. |
502 Bad Gateway |
Шлюз или прокси-сервер получили неверный ответ от сервера за ним. | Да, если запрос является безопасным для повторения |
503 Service Unavailable |
Сервер временно перегружен или недоступен из-за технического обслуживания, и он должен восстановиться через некоторое время. Сервер может отправить Retry-After заголовок. |
Да, по истечении Retry-After времени, если сервер его отправил |
504 Gateway Timeout |
Шлюз или прокси-сервер не получил ответ вовремя с сервера за ним | Да, если запрос является безопасным для повторения. Шлюз отказался от ожидания, поэтому вы не знаете, выполнил ли сервер операцию. |
Какие запросы безопасны для повторных попыток
RFC 9110 называет метод идемпотентным, когда отправка одного и того же запроса несколько раз имеет тот же эффект, что и его однократная отправка.
GET, HEAD, OPTIONS, TRACE, PUT и DELETE идемпотентны.
POST и PATCH не являются таковыми. Согласно RFC, клиент не должен автоматически повторять запрос с помощью метода, отличного от идемпотентного, если он не знает, что запрос в любом случае идемпотентен, или не может определить, что сервер никогда не обрабатывал исходный запрос. Дополнительные сведения см. в разделе "Идемпотентные методы".
Повторный запрос POST после 502 или 504 может создать второй заказ или отправить второе сообщение электронной почты. Некоторые библиотеки повторных попыток по умолчанию повторно выполняют любой вызов метода. Например, стандартный обработчик устойчивости .NET повторяет попытку POST, если только вы не вызовете options.Retry.DisableForUnsafeHttpMethods(). Дополнительные сведения см. в разделе Создание устойчивых HTTP-приложений.
Retry-After на 503
503 может включать Retry-After заголовок. Его значением является либо количество секунд, например 120, либо дата HTTP, например Fri, 31 Dec 1999 23:59:59 GMT. Код должен обрабатывать оба. Дополнительные сведения см. в разделе Retry-After.
Прекратите вызывать API, который постоянно завершается с ошибкой
Повторные попытки помогают при кратковременных сбоях. Когда API недоступен в течение нескольких минут, повторная попытка каждого запроса добавляет нагрузку на сервер, который уже борется, и пользователи ожидают, пока каждая повторная попытка завершится ошибкой. Паттерн Circuit Breaker отслеживает сбои, и когда сбоев становится слишком много, он на некоторое время прекращает обращения к API и быстро возвращает ошибку. После этого он пропускает несколько запросов, чтобы проверить, восстановился ли API. Дополнительные сведения см. в разделе «Паттерн „Автоматический выключатель“». Стандартный обработчик устойчивости .NET включает в себя автоматический выключатель, который открывается на 5 секунд, когда не менее 10% запросов завершаются сбоем в 30-секундном окне с по крайней мере 100 запросов.
Обработка ошибок 5xx
- Повторите попытку только для идемпотентных запросов при ответах 502, 503 и 504. Для
POSTиPATCHповторите попытку только в том случае, если в документации API описан способ сделать их безопасными для повторного выполнения. - Подождите, прежде чем повторить попытку. Используйте
Retry-After, когда сервер отправляет его. В противном случае используйте экспоненциальную задержку со случайным джиттером и прекратите после нескольких попыток. - Ознакомьтесь с документацией API по ошибке 500. Повторите попытку только в том случае, если API говорит, что это безопасно.
- Остановите вызов API, который постоянно завершается с ошибкой. Используйте механизм Circuit Breaker, чтобы приложение быстро завершалось с ошибкой, пока API восстанавливается.
- Сообщите пользователю, что произошло. Показывайте «в работе службы возникли проблемы, повторите попытку позже» вместо универсальной ошибки или трассировки стека.
const RETRYABLE_STATUS = new Set([502, 503, 504]);
const IDEMPOTENT_METHODS = new Set(["GET", "HEAD", "OPTIONS", "TRACE", "PUT", "DELETE"]);
function retryAfterMs(response) {
const value = response.headers.get("retry-after");
if (!value) return null;
const seconds = Number(value);
if (Number.isFinite(seconds)) return seconds * 1000;
const date = Date.parse(value);
return Number.isNaN(date) ? null : Math.max(0, date - Date.now());
}
export async function fetchWithRetry(url, options = {}, maxRetries = 3) {
const method = (options.method ?? "GET").toUpperCase();
const canRetry = IDEMPOTENT_METHODS.has(method);
for (let attempt = 0; ; attempt++) {
const response = await fetch(url, options);
if (!RETRYABLE_STATUS.has(response.status) || !canRetry || attempt === maxRetries) {
return response;
}
await response.body?.cancel();
const backoff = 2 ** attempt * 1000 + Math.random() * 1000;
const wait = retryAfterMs(response) ?? backoff;
await new Promise((resolve) => setTimeout(resolve, wait));
}
}
Как проверить, что приложение обрабатывает ошибки 5xx
Вы редко видите 5xx во время разработки, и вы не можете заставить API выдавать ошибку по запросу. Способ тестирования определяет, находят ли ошибки раньше, чем ваши пользователи.
| Approach | Что вы найдете | То, что вы пропустили |
|---|---|---|
| Ожидание production | Реальные сбои | Всё, пока пользователь не нажмёт |
| Мокируйте API в тестах или позвольте ИИ-агенту для программирования написать мок | Выполняется ли ветвь обработки ошибок | Реальный HTTP-клиент и библиотека повторных попыток, а также сколько раз она фактически выполняет повторные попытки. Приложению также требуется переключатель только для тестирования, чтобы получить доступ к mock-сервису. |
| Вызовите реальный API и дождитесь сбоя | Реальное поведение | Невозможно вызвать сбой API по запросу |
| Перехватывайте реальный трафик приложения и возвращайте ошибки 5xx с выбранной вами частотой | Реальный HTTP-клиент, библиотека повторных попыток и предохранитель | Ничего в приложении не изменяется, поэтому ваш код не тестируется изолированно. Оставьте модульные тесты для этого. |
Попробуйте его в вашем приложении
Прокси-сервер разработки перехватывает запросы приложения и завершает их сбоем с заданными ошибками с помощью GenericRandomErrorPlugin. Приложение продолжает вызывать реальный URL-адрес. Добавьте подключаемый модуль в файл конфигурации и укажите для его errorsFile файл с ошибками 5xx. В этом примере используется https://api.contoso.com. Замените его на URL-адрес API, который вызывает ваше приложение.
Файл: server-errors.json
{
"$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v3.3.1/genericrandomerrorplugin.errorsfile.schema.json",
"errors": [
{
"request": {
"url": "https://api.contoso.com/*"
},
"responses": [
{ "statusCode": 500 },
{ "statusCode": 502 },
{
"statusCode": 503,
"headers": [
{ "name": "Retry-After", "value": "10" }
]
},
{ "statusCode": 504 }
]
}
]
}
По умолчанию сбой происходит в 50% запросов. Проверьте в выходных данных Dev Proxy, что приложение повторяет запросы GET и отправляет каждый POST только один раз. Прокси-сервер разработки Retry-After не проверяет, ожидает ли ваше приложение ответа 503, поэтому сравнивайте время запроса самостоятельно. Затем запустите Dev Proxy с помощью --failure-rate 100, чтобы узнать, что делает ваше приложение, когда API продолжает выдавать сбои. Дополнительные сведения см. в разделе Частота сбоев запросов на изменение. Чтобы установить Dev Proxy, см. статью "Настройка Dev Proxy".