Можем ли мы создать исключение в резервной или резервной фабрике @FeignClient?

Я использую @FeignClient и хочу выполнить некоторую логику (например, записать информацию об исключении), когда Feign выдает исключение, а затем отправляет результат во внешний интерфейс.

Я заметил, что Feign выдает FeignException при сбое подключения или неожидании статуса http.

Поэтому я определил @ExceptionHandler для перехвата FeignException после вызова метода обратного вызова.

    @ExceptionHandler(value = FeignException.class)
    @ResponseBody
    public ResponseResult feignException(FeignException exception){
        String message = exception.getMessage();
        byte[] content = exception.content();
        int status = exception.status();
        if(content!=null){
            String response=new String(content);
            message=String.format("%s response message : %s",message,response);
        }
        log.warn("{} : {} , cause by : {}",exception.getClass().getSimpleName(),message,exception.getCause());
        return ResponseResult.fail(HttpStatus.valueOf(status),String.format("9%s00",status),message);

Но это не может быть перехвачено, когда я устанавливаю обратный вызов или callbackFactory для @FeignClient.

    @FeignClient(url = "${onboardingcase.uri}",name = "OnBoardingCaseService",
            fallbackFactory = OnBoardingCaseServiceFallBack.class)


@Component
@Slf4j
public class OnBoardingCaseServiceFallBack implements FallbackFactory<OnBoardingCaseService> {

    @Override
    public OnBoardingCaseService create(Throwable throwable) {
        return new OnBoardingCaseService() {
            @Override
            public OnBoardingCaseVo query(String coid) {

                if(throwable instanceof FeignException){
                    throw (FeignException)throwable;
                }
                return null;
            }
        };
    }
}

Я заметил, потому что hystrix взял на себя этот метод. И перехватит исключение в HystrixInvocationHandler.

try {
                                Object fallback = HystrixInvocationHandler.this.fallbackFactory.create(this.getExecutionException());
                                Object result = ((Method)HystrixInvocationHandler.this.fallbackMethodMap.get(method)).invoke(fallback, args);
                                if (HystrixInvocationHandler.this.isReturnsHystrixCommand(method)) {
                                    return ((HystrixCommand)result).execute();
                                } else if (HystrixInvocationHandler.this.isReturnsObservable(method)) {
                                    return ((Observable)result).toBlocking().first();
                                } else if (HystrixInvocationHandler.this.isReturnsSingle(method)) {
                                    return ((Single)result).toObservable().toBlocking().first();
                                } else if (HystrixInvocationHandler.this.isReturnsCompletable(method)) {
                                    ((Completable)result).await();
                                    return null;
                                } else {
                                    return HystrixInvocationHandler.this.isReturnsCompletableFuture(method) ? ((Future)result).get() : result;
                                }
                            } catch (IllegalAccessException var3) {
                                throw new AssertionError(var3);
                            } catch (ExecutionException | InvocationTargetException var4) {
                                throw new AssertionError(var4.getCause());
                            } catch (InterruptedException var5) {
                                Thread.currentThread().interrupt();
                                throw new AssertionError(var5.getCause());
                            }

Итак, я хочу знать, как я могу создать исключение, когда использую callback/callbackFactory, или есть другой способ вместо callbackFactory сделать «обратный вызов»?

Огромное спасибо


person chris    schedule 07.01.2020    source источник


Ответы (2)


Я нашел решение этой проблемы.

public class OnBoardingCaseServiceFallBack implements FallbackFactory<OnBoardingCaseService> {

    @Override
    public OnBoardingCaseService create(Throwable throwable) {
        return new OnBoardingCaseService() {
            @Override
            public OnBoardingCaseVo query(String coid) {
                log.error("OnBoardingCaseService#query fallback , exception",throwable);
                if(throwable instanceof FeignException){
                    throw (FeignException)throwable;
                }

                return null;
            }
        };
    }
}

А затем поймал HystrixRuntimeException и получил причину исключения в ExceptionHandler для получения реального исключения, которое было обернуто Hystrix.

    @ExceptionHandler(value = HystrixRuntimeException.class)
    @ResponseBody
    public ResponseResult hystrixRuntimeException(HystrixRuntimeException exception){
        Throwable fallbackException = exception.getFallbackException();
        Throwable assertError = fallbackException.getCause();
        Throwable realException = assertError.getCause();
        if(realException instanceof FeignException){
            FeignException feignException= (FeignException) realException;
            String message = feignException.getMessage();
            byte[] content = feignException.content();
            int status = feignException.status();
            if(content!=null){
                String response=new String(content);
                message=String.format("%s response message : %s",message,response);
            }
            return ResponseResult.fail(HttpStatus.valueOf(status),String.format("9%s00",status),message);
        }
        String message = exception.getMessage();
        log.warn("{} : {} , cause by : {}",exception.getClass().getSimpleName(),message,exception.getCause());
        return ResponseResult.fail(ResultCode.FAIL.httpStatus(),ResultCode.FAIL.code(),message);
    }

Но я не думаю, что это хороший способ~

person chris    schedule 13.01.2020
comment
Я сделал это так же, но исключение без hysterix просто обычное проверенное исключение с Async + Feign - person Taranjit Kang; 27.01.2021

Я никогда не делал этого в качестве резервного варианта, я реализовал собственный класс декодера ошибок («CustomFeignErrorDecoder») и расширил feign.codec.ErrorDecoder, каждый раз, когда возникает ошибка, она приходит к этому классу.

В функции decode создайте пользовательское исключение и перехватите его на уровне контроллера или сервиса, чтобы показать ваше сообщение во внешнем интерфейсе.

Пример:

@Component
public class CustomFeignErrorDecoder implements ErrorDecoder {

   @Override
   public Exception decode(String methodKey, Response response) {
       throw new CustomFeignErrorDecoderException(methodKey +" response status "+ response.status() +" request "+ response.request()+ " method "+ response.request().httpMethod());
   }
}
person Pavan Kumar Jorrigala    schedule 09.01.2020
comment
Спасибо, Паван. Я внедрил errorDecoder, как и вы, но обнаружил, что процесс представляет собой пользовательский метод обратного вызова декодера ошибок-> (если метод обратного вызова был реализован). И он будет обернут HystrixRuntimeException, если мы выдадим исключение в методе обратного вызова. Поэтому я попытался поймать HystrixRuntimeException, а затем проанализировать причину. И тогда я могу получить исключение, которое я настраиваю. - person chris; 13.01.2020
comment
Я добавил реализацию. Возможно, попробуйте добавить свойство Hystix timeoutInMilliseconds, если вы поделитесь минимальным кодом, который я могу воспроизвести в своем локальном компьютере, чтобы увидеть проблему. - person Pavan Kumar Jorrigala; 14.01.2020
comment
Исключение RuntimeException, которое возникает при сбое HystrixCommand и не имеет резервного варианта в соответствии с HystrixRuntimeException - person Pavan Kumar Jorrigala; 14.01.2020
comment
Да, Паван. Итак, мой дизайн выдает собственное исключение для резервного метода. А затем используйте @ExceptionHandler(value = HystrixRuntimeException.class), чтобы поймать HystrixRuntimeException. И я могу получить пользовательское исключение в HystrixRuntimeException. что ты думаешь ? это хороший способ или нет? - person chris; 16.01.2020
comment
Я показал код в другом ответе. Вы можете открыть страницу и проверить ее. спасибо - person chris; 16.01.2020
comment
Чтобы ответить на ваш вопрос, я хочу знать тайм-аут Hystrix, который вы использовали. Мой ответ для FeignClient без ложных откатов. Hystrix используется для автоматических выключателей, чтобы идентифицировать отказавшие узлы и удерживать их в течение некоторого времени вне цикла. Feign предназначен для обработки SockerException. Если во время отдыха возникает какая-либо ошибка, вы можете поймать ее в ErrorDecoder и распространить. Как правило, если тайм-аут симуляции составляет 2 секунды (с возможными 4-5 повторными попытками), то тайм-аут Hystrix будет около 10 секунд. Можете ли вы попробовать прокомментировать Hystrix и посмотреть, работает ли он? - person Pavan Kumar Jorrigala; 16.01.2020
comment
Привет, Паван. Я закрыл тайм-аут hystrix. используется hystrix: команда: по умолчанию: выполнение: тайм-аут: включено: false. Потому что я просто хочу использовать метод восстановления после сбоя при симуляции исключения. и не нужна никакая функция hystix. - person chris; 16.01.2020