HTTP retry в Go: почему один список кодов не делает повтор безопасным
После сетевой ошибки сервис повторил POST и создал второй заказ. Решение о retry нельзя принимать только по коду ответа. Важно, является ли операция безопасной для повторения и известно ли, выполнилась ли первая попытка.
Как разобрать ситуацию
Разделите политику по методу, смыслу операции и контракту зависимости. Для чтения допустим ограниченный повтор некоторых временных отказов; изменение состояния требует отдельного механизма идемпотентности. Кроме количества попыток задайте общий deadline, задержку и обработку серверного ограничения скорости. Бесконечные повторы превращают временную перегрузку в устойчивую.
Пример и ожидаемый результат
В примере проверяется упрощённая локальная политика: повторяем GET при 502 или 503, а POST не повторяем автоматически. Это демонстрация проверки правил, а не готовая библиотека повторов. Для 429 нужна отдельная обработка указаний поставщика.
package main
import "fmt"
func retry(method string, status int) bool {
return method == "GET" && (status == 502 || status == 503)
}
func main() { fmt.Println(retry("GET", 503), retry("POST", 503), retry("GET", 400)) }
Программа выводит true, false, false: GET/503 допускается, POST/503 и GET/400 отклоняются выбранной политикой.
Проверка перед применением
- Смоделируйте ошибку после фактического создания заказа, а не только до отправки.
- Проверьте, воспроизводимо ли тело запроса для второй попытки.
- Запишите число попыток и суммарную задержку, чтобы повтор не выходил за бюджет входящего запроса.
Полезный тест надёжности — одинаковый бизнес-запрос с разными местами сбоя: до обработки, после записи и до получения ответа. Во всех случаях проверьте итоговое состояние, а не только количество успешных HTTP-ответов. Дешёвая повторная попытка может оказаться дорогой двойной операцией, если измерять лишь сетевую доступность.
Границы решения
GET не является магическим разрешением повторять любую бизнес-операцию: неверно спроектированный API может менять состояние при чтении. Полный клиент должен учитывать контракт, отмену, тело ответа и ограничения поставщика.
Если базовый Go уже знаком и хочется связать HTTP, хранение данных и обработку отказов в одну систему, посмотрите наш платный курс
Микросервисы на Go: HTTP, PostgreSQL, gRPC, Kafka и Docker. Перед записью сравните программу со своей задачей и подготовкой.
Техническая справка: pkg.go.dev: документация, www.rfc-editor.org: документация.
Следующий шаг: Go backend: HTTP, конкурентность и устойчивость сервиса. По смежным вопросам: Общий счётчик в Go: почему обычный плюс не безопасен между goroutine, Отмена HTTP-запроса в Go: как убедиться, что context дошёл до клиента.
Продолжить на практике
- Микросервисы на Go: HTTP, PostgreSQL, gRPC, Kafka и Docker ↗
От работающего Go API — к взаимодействию сервисов, которому можно доверять. Разберитесь, как проектировать контракты, сохранять согласованность данных и восстанавливать обработку после сбоев. HTTP, PostgreSQL, gRPC, Kafka и Docker — через код и наглядные разборы.
