Притворные клиенты облегчают написание клиента restful на основе аннотаций Spring, которые вы уже знаете. Он также включает интеграцию с множеством других библиотек netflix и хорошими шаблонами микросервисов (такими как обнаружение служб, балансировка нагрузки и автоматические выключатели).
Feign настраивается, но обычно вы хотите изменить конфигурацию, прежде чем использовать ее для вызовов службы в службу.
Регистрация
Вы захотите посмотреть, какие запросы делаются. Для этого вам необходимо сделать две вещи: установить уровень регистратора для конфигурации feign в вашем приложении.yml и установите уровень ведения журнала клиентского класса feign для ОТЛАДКИ.
logger.level: root: INFO com.example.clients.InvoiceClient: DEBUG feign: client: config: default: loggerLevel: basic
Вы можете установить уровень регистратора
до полного
если вы тоже хотите видеть заголовки и тела ответов в журналах.
Следует ли декодировать 404-е
Если вы хотите, чтобы при получении вашим клиентом ответа 404 “Не найдено” возникало исключение FeignException, вы можете установить для декодера 404 значение false, которое используется по умолчанию. В противном случае, если вы установите для декодера 404 значение true, вы получите значение null в качестве ответа или необязательно, если вы включили типы возвращаемых данных в опции.
Декодер ошибок
Как бороться с ошибками – это самое важное, потому что вы не будете возвращать разумные ответы своему клиенту, если он у вас есть, и облегчите расследование, когда что-то пойдет не так. Я считаю, что разумное, что нужно сделать, это:
- повторите попытку при любой ошибке сервера (статус > 499)
- возвращает ту же ошибку сервера когда повторная попытка исчерпана
- повторите попытку на любом 429 или когда установлен заголовок Повторной попытки
- возвращайте 500 при возникновении любой другой ошибки клиента
- зарегистрируйте ошибку со статусом, какой метод ее вызвал и орган реагирования
Это довольно внушительный список требований. Давайте создадим декодер ошибок, который удовлетворяет этому:
public class CustomErrorDecoder implements ErrorDecoder { private static final Logger LOG = LoggerFactory.getLogger(CustomErrorDecoder.class); private ErrorDecoder defaultDecoder = new ErrorDecoder.Default(); @Override public Exception decode(String methodKey, Response response) { //Requirement 5: log error first and include response body try { LOG.error("Got {} response from {}, response body: {}", response.status(), methodKey, IOUtils.toString(response.body().asReader())); } catch (IOException e) { LOG.error("Got {} response from {}, response body could not be read", response.status(), methodKey); } Exception defaultException = defaultDecoder.decode(methodKey, response); if (defaultException instanceof RetryableException.class) { //Requirement 3: retry when Retry-After header is set //Will be true if Retry-After header is set e.g. in case of 429 status return defaultException; } if (HttpStatus.valueOf(response.status()).is5xxServerError()) { //Requirement 1: retry on server error return new RetryableException("Server error", response.request().httpMethod(), new ServerErrorResponseFromOtherSystemException(HttpStatus.valueOf(response.status()), defaultException), null); } else { //Requirement 4: return 500 on client error return new ClientErrorResponseFromOtherSystemException("Client error " + response.status() + " from calling other system", defaultException); } } }
И исключения в бросках:
//Requirement 4: return 500 on client error @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) public class ClientErrorResponseFromOtherSystemException extends Exception { public ThirdPartyClientErrorResponseException(String message, Exception exception) { super(message, exception); } } public class ServerErrorResponseFromOtherSystemException extends Exception { private HttpStatus responseStatusFromOtherSystem; public ServerErrorResponseFromOtherSystemException(HttpStatus httpStatus, Exception exception) { this.responseStatusFromOtherSystem = httpStatus; super(message, exception); } public HttpStatus getStatus() { return responseStatusFromOtherSystem; } }
Аннотация @ResponseStatus
означает, что spring вернет этот статус, когда аннотированное исключение достигнет контроллера. Чтобы определить статус ответа Ответ на ошибку сервера От другого SystemException
нам нужен обработчик исключений для контроллера:
@RestController public class InvoiceController { //Handler methods @ExceptionHandler(ServerErrorResponseFromOtherSystemException.class) public void ResponseEntity handleServerErrorResponseException(ServerErrorResponseFromOtherSystemException ex) { //Requirement 2: return the same error when retry is exhausted return ResponseEntity.status(exception.getStatus()).build(); } }
Чтобы притвориться, что вы используете повторитель, его нужно будет выставить в виде боба. Чтобы иметь возможность настроить политику распространения исключений на разворачивание, нам также необходимо предоставить конструктор симуляции с нашими настройками в качестве компонента. Смотрите пример ниже:
@Bean public Retryer retryer() { return new Retryer.Default(); } @Bean public Feign.Builder feignBuilder(Retryer retryer) { return Feign.builder() .exceptionPropagationPolicy(ExceptionPropagationPolicy.UNWRAP) .errorDecoder(new ServerErrorRetryingErrorDecoder()) .retryer(retryer); }
Установка политики распространения исключений на разворачивание означает, что исключение внутри Повторяющегося исключения
будет создаваться при исчерпании повторных попыток, позволяя создавать исключения для конкретных приложений, а затем обрабатываться обработчиками исключений (опять же, при условии, что вы используете Spring MVC).
Изящная обработка ошибок имеет важное значение, и создание ее с самого начала является хорошей практикой – не ждите, пока клиенты заметят, что системы сторонних производителей выходят из строя. Я обнаружил, что даже самые надежные сторонние системы могут иногда прерывать работу и возвращать 502. Это случилось со мной на производстве, где произошла ошибка шлюза, которую я никогда не видел в других средах. К счастью, я внедрил этот механизм повторной попытки, и запрос был повторен, и клиент продолжил путешествие после небольшого ожидания.
Оригинал: “https://dev.to/philhardwick/sensible-feign-client-configuration-ma3”