【问题标题】:Can we throw an exception in fallback or fallbackFactory of @FeignClient我们可以在@FeignClient 的 fallback 或 fallbackFactory 中抛出异常吗
【发布时间】:2020-04-24 20:23:48
【问题描述】:

我正在使用@FeignClient,想在Feign抛出异常时做一些逻辑(比如记录异常信息),然后将结果回复给前端。

我注意到当连接失败或 http 状态不期望时,Feign 会抛出 FeignException。

所以我定义了一个@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);

但是当我设置@FeignClient的回调或者callbackFactory的时候就捕捉不到了。

    @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());
                            }

所以我想知道当我使用回调/回调工厂时如何抛出异常,或者有另一种方法来代替回调工厂来执行“回调”?

非常感谢

【问题讨论】:

    标签: spring-cloud hystrix spring-cloud-feign feign


    【解决方案1】:

    我找到了解决这个问题的方法。

    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封装的realException。

        @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);
        }
    

    但我觉得这不是个好办法~

    【讨论】:

    • 我也是这样做的,但非 hysterix 异常只是 Async + Feign 的常规检查异常
    【解决方案2】:

    我从来没有在后备中这样做过,我已经实现了自定义错误解码器(“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());
       }
    }
    

    【讨论】:

    • 谢谢帕万。我已经像您一样实施了errorDecoder,但我发现该过程是自定义错误解码器->回调方法(如果实现了回调方法)。如果我们在回调方法上抛出异常,它将被 HystrixRuntimeException 包装。所以我试图捕获 HystrixRuntimeException 然后解析原因。然后我可以得到我自定义的异常。
    • 我添加了实现,也许可以尝试添加 Hystix 属性“timeoutInMilliseconds”,如果你分享一个我可以在本地复制的最小代码来查看问题。
    • 当 HystrixCommand 失败且没有回退时抛出的 RuntimeException 根据HystrixRuntimeException
    • 是的,帕万。所以我的设计是在回退方法上抛出一个自定义异常。然后使用@ExceptionHandler(value = HystrixRuntimeException.class) 捕获HystrixRuntimeException。我可以在 HystrixRuntimeException 中获取自定义异常。你怎么看 ?这是个好方法吗?
    • 我在另一个答案中显示了代码。您可以拉下页面并检查它。谢谢
    猜你喜欢
    • 2011-03-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-09
    • 1970-01-01
    • 1970-01-01
    • 2014-07-29
    • 1970-01-01
    相关资源
    最近更新 更多