Как обработать ошибку 429 Too Many Requests в Spring при работе с REST клиентом?
Что проверяют
Умеете ли вы отличить временную ошибку от постоянной и повторить запрос так, чтобы не добить и без того перегруженный сервис. Следом почти всегда спрашивают про заголовок Retry-After и про то, какие запросы вообще можно повторять.
Как отвечать
Сначала поймать статус. RestTemplate на 4xx бросает HttpClientErrorException, для 429 есть подкласс HttpClientErrorException.TooManyRequests. В RestClient (Spring Framework 6.1 и новее) реакцию описывают в цепочке: .onStatus(status -> status.value() == 429, handler), где обработчик бросает своё исключение с данными из заголовков. В WebClient это onStatus плюс retryWhen.
Дальше стратегия повтора:
Retry-After: если сервер его прислал, ждать столько, сколько сказано. Значение бывает числом секунд или HTTP-датой;- экспоненциальная задержка с джиттером: без заголовка ждём 1, 2, 4 секунды со случайным разбросом, чтобы клиенты не вернулись одновременно;
- лимит попыток и общий дедлайн: три попытки, потом ошибка наверх, а не бесконечный цикл;
- идемпотентность:
GETиPUTповторяем спокойно,POSTтолько с ключом идемпотентности.
Инструменты: в Resilience4j Retry с IntervalBiFunction, которая достаёт Retry-After из исключения, в Spring Retry @Retryable с @Backoff, в WebClient оператор Retry.backoff() из Reactor. В Spring Framework 7 собственный @Retryable появился прямо в ядре. А лучше не доводить до 429: клиентский RateLimiter из Resilience4j держит темп ниже квоты внешнего API.
Где спотыкаются
Делают Thread.sleep и рекурсивный вызов в обработчике ошибки. Поток запроса висит, глубина рекурсии не ограничена, и при долгом ограничении сервис сам исчерпывает пул потоков.
Проверяют код через getRawStatusCode() у RestClientException. У базового исключения такого метода нет, он есть у RestClientResponseException, а в Spring 6 вместо него используют getStatusCode().
Повторяют сразу, без паузы и без учёта Retry-After. Для многих API это повод продлить ограничение или заблокировать ключ, и временная ошибка становится постоянной.