赞
踩
feign异常处理。
本文针对feign调用过程中,发生异常情况时的逻辑处理流程进行梳理,为之后遇到相关异常情况时,心里有底。
首先介绍下作为背景的测试用例。
// ================================================================== // ================================================== 配置 ========== // ================================================================== // ========================== 配置1 // 使用 fallbackFactory属性配置了自定义hystrix异常处理逻辑 @FeignClient(value = "projectB", fallbackFactory = FeignCallServiceFallbackFactory.class) public interface FeignSpringAnnotationCallService { @RequestMapping(value = "/projectB/{name}", method = RequestMethod.GET) String call(@PathVariable(value = "name") String name); } // ========================== 配置2 // 定义feign的全局异常处理接口ErrorDecoder实现类 // override {@code FeignClientsConfiguration} 和 {@code FeignClientFactoryBean.getInheritedAwareOptional(context, ErrorDecoder.class)} @Bean public FeignErrorDecoder feignErrorDecoder() { // 在 AsyncResponseHandler 中应用 return new FeignErrorDecoder(); } public static class FeignErrorDecoder implements ErrorDecoder { @Override public Exception decode(String methodKey, Response response) { int status = response.status(); if (methodKey.contains("#callFail2")) { // 如果这里抛出异常, 将被转给hystrix异常处理 throw new RuntimeCryptoException("callFail2-LQ"); } if (status >= HttpStatus.BAD_REQUEST.value() && status <= HttpStatus.INTERNAL_SERVER_ERROR.value()) { String message = response.reason(); // 获取原始错误信息 try (Reader reader = response.body().asReader(Charset.defaultCharset())) { message = CharStreams.toString(reader); } catch (Exception ignored) { status = HttpStatus.EXPECTATION_FAILED.value(); log.error(HttpStatus.EXPECTATION_FAILED.getReasonPhrase(), ignored); } // return new KqHystrixBadRequestException(status,methodKey + " " + message); return new KqHystrixBadRequestException(status, message); } return errorStatus(methodKey, response); } } // ================================================================== // ================================================== 调用 ========== // ================================================================== @Slf4j @RestController public class HelloController { @Autowired private FeignSpringAnnotationCallService projectBServiceCall; @PostMapping("/hello/{name}") public String feignCall(@PathVariable String name) { return projectBServiceCall.call(name); } }
以上测试用例中,调用端没有任何需要特别说的地方,重点还是在第一步的配置中:
所以,本文所要解决的问题就是:
跟随以上这两个问题,在我们之前feign源码阅读1,feign源码阅读2基础上,看看以上两个配置项是如何生效的。
根据之前的博客feign源码解析 - 初始化,我们可以很快定位出如下代码:
// 在前面博客中我们介绍了, 针对每个@FeignClient注解的接口, 会创建一个对应的FeignClientFactoryBean实例用来生成@FeignClient注解所修饰的接口的代理类. class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean, ApplicationContextAware { /*********************************** * WARNING! Nothing in this class should be @Autowired. It causes NPEs because of some * lifecycle race condition. ***********************************/ ...... // FeignClientsRegistrar.registerFeignClient(...)实现中会读取注解@FeignClient中配置的 fallbackFactory, fallback属性值。赋值给FeignClientFactoryBean类型实例对应属性 private Class<?> fallback = void.class; private Class<?> fallbackFactory = void.class; ...... protected void configureUsingConfiguration(FeignContext context, Feign.Builder builder) { ...... // 从Spring容器中查找是否有用户自定义的ErrorDecoder实现类 ErrorDecoder errorDecoder = getInheritedAwareOptional(context, ErrorDecoder.class); if (errorDecoder != null) { builder.errorDecoder(errorDecoder); } else { FeignErrorDecoderFactory errorDecoderFactory = getOptional(context, FeignErrorDecoderFactory.class); if (errorDecoderFactory != null) { ErrorDecoder factoryErrorDecoder = errorDecoderFactory.create(type); builder.errorDecoder(factoryErrorDecoder); } } ...... if (decode404) { builder.decode404(); } } }
根据之前的博客feign源码解析 - 运行时,我们可以很快定位出如下代码:
// ===================== HystrixInvocationHandler.java // 因为我们启用了 feign.hystrix.enabled=true(在 HystrixFeignConfiguration 中生效), 所以最终代理类为HystrixInvocationHandler, 而非默认的ReflectiveFeign.FeignInvocationHandler. @Override public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable { // early exit if the invoked method is from java.lang.Object // code is the same as ReflectiveFeign.FeignInvocationHandler // 如果调用的是Object定义的系类方法, 诸如toString()等 ...... // 对于每个定义的feign方法,都重新构建一个新的自定义HystrixCommand类型的实例. // HystrixCommand类型覆写了基类AbstractCommand的isFallbackUserDefined()方法,用来托底fallback —— 即判断当前的自定义HystrixCommand类型中是否定义了"getFallback"方法(这里采用的是反射方式,估计是为了兼容) // 这里自定义的HystrixCommand则正好会覆写基类的getFallback(),和上面一行呼应上了. 齿轮严丝合缝地咬合上了. HystrixCommand<Object> hystrixCommand = new HystrixCommand<Object>(setterMethodMap.get(method)) { @Override protected Object run() throws Exception { try { // 跨线程调用, 这里最终调用的还是 SynchronousMethodHandler return HystrixInvocationHandler.this.dispatch.get(method).invoke(args); } catch (Exception e) { throw e; } catch (Throwable t) { throw (Error) t; } } @Override protected Object getFallback() { if (fallbackFactory == null) { return super.getFallback(); } try { Object fallback = fallbackFactory.create(getExecutionException()); Object result = fallbackMethodMap.get(method).invoke(fallback, args); ...... } catch (Exception e) { ...... } } }; ...... return hystrixCommand.execute(); }
final class SynchronousMethodHandler implements MethodHandler { @Override public Object invoke(Object[] argv) throws Throwable { RequestTemplate template = buildTemplateFromArgs.create(argv); Options options = findOptions(argv); Retryer retryer = this.retryer.clone(); while (true) { try { return executeAndDecode(template, options); } catch (RetryableException e) { try { // 默认是"永不重试", 直接进入接下来的catch()块, 然后抛出异常.没希望进入下面的 continue. 异常处理流程进入hystrix体系下. retryer.continueOrPropagate(e); } catch (RetryableException th) { Throwable cause = th.getCause(); if (propagationPolicy == UNWRAP && cause != null) { throw cause; } else { throw th; } } if (logLevel != Logger.Level.NONE) { logger.logRetry(metadata.configKey(), logLevel); } continue; } } } Object executeAndDecode(RequestTemplate template, Options options) throws Throwable { Request request = targetRequest(template); if (logLevel != Logger.Level.NONE) { logger.logRequest(metadata.configKey(), logLevel, request); } Response response; long start = System.nanoTime(); try { response = client.execute(request, options); // ensure the request is set. TODO: remove in Feign 12 response = response.toBuilder() .request(request) .requestTemplate(template) .build(); } catch (IOException e) { // 只接受IOException异常 if (logLevel != Logger.Level.NONE) { logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime(start)); } // 封装为 RetryableException 再次抛出, 供上层的invoke(...)捕获,用作重试条件 throw errorExecuting(request, e); } long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start); if (decoder != null) return decoder.decode(response, metadata.returnType()); CompletableFuture<Object> resultFuture = new CompletableFuture<>(); // 自定义ErrorDecoder实现类在这里生效 asyncResponseHandler.handleResponse(resultFuture, metadata.configKey(), response, metadata.returnType(), elapsedTime); try { if (!resultFuture.isDone()) throw new IllegalStateException("Response handling not done"); return resultFuture.join(); } catch (CompletionException e) { Throwable cause = e.getCause(); if (cause != null) throw cause; throw e; } } }
让我们来做个总结,忽略掉细节,形成全局认识。
端 | 依据 | 失败时的处理者 |
---|---|---|
服务端报错 | HTTP返回状态码为非200-300之间 (请求被正常发出,并且被如期接收到) | 失败则走ErrorDecoder 自定义实现类 |
客户端错误 | 典型如超时,熔断条件被触发。 1. 未找到对应服务异常 com.netflix.client.ClientException: Load balancer does not have available server for client: projectB 2. 超时异常 java.net.SocketTimeoutException: connect timed out 3. 未找到目标服务异常 feign.RetryableException: Failed to connect to /127.0.0.1:80 executing GET http://127.0.0.1/xxx/user/select/getPersonnelById | 一律走 hystrix fallback —— FallbackFactory 自定义实现类 |
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。