Как повысить надежность отправки запросов при недоступности стороннего сервиса
Что проверяют
Знаете ли вы приёмы отказоустойчивости и, главное, понимаете ли, где каждый из них вредит: бездумные повторы добивают и так лежащий сервис. Следом обычно спрашивают, что будет с запросом, который обязан дойти, если партнёр недоступен час.
Как отвечать
Разделите ответ на синхронную защиту и гарантированную доставку. Для первой в Spring обычно берут Resilience4j:
- таймауты: всегда явные. У
java.net.http.HttpClientэтоconnectTimeoutпри сборке клиента иtimeoutуHttpRequest, по умолчанию таймаута на ответ нет, и поток может ждать неограниченно долго. УRestClientиWebClientони задаются через фабрику запросов или коннектор; Retry: только для временных ошибок: обрыв соединения, 502, 503, 504, 429 с учётомRetry-After. Задержка экспоненциальная со случайным разбросом (jitter), иначе все клиенты ударят одновременно. 400 и 404 не повторяют;CircuitBreaker: когда доля отказов превышает порог, перестаёт звать сервис и сразу отдаёт ошибку или запасной ответ, позже пропускает пробные запросы в полуоткрытом состоянии;Bulkhead: ограничивает число одновременных вызовов, чтобы медленный партнёр не занял все потоки приложения;- fallback: кэш последнего ответа, значение по умолчанию или честная деградация функции.
Если запрос должен дойти обязательно, синхронных повторов мало. Помогает transactional outbox: сообщение пишется в свою таблицу в той же транзакции, отдельный обработчик отправляет его с повторами, а после исчерпания попыток оно уходит в очередь недоставленных. Повторы должны быть безопасны, поэтому к запросу добавляют ключ идемпотентности. Метрики через Micrometer покажут, когда размыкается автомат.
Где спотыкаются
Повторяют всё подряд, включая неидемпотентный POST без ключа. Итог: двойное списание или два заказа у партнёра.
Ставят повторы без jitter и без лимита. При сотне инстансов это превращается в атаку на восстанавливающийся сервис.
Предлагают Hystrix. Он с 2018 года в режиме поддержки, в современных проектах его место занял Resilience4j.