feign异常处理。
1. 前言
本文针对feign调用过程中,发生异常情况时的逻辑处理流程进行梳理,为之后遇到相关异常情况时,心里有底。
2. 用例
首先介绍下作为背景的测试用例。
// ==================================================================
// ================================================== 配置 ==========
// ==================================================================
// ========================== 配置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);
}
}
以上测试用例中,调用端没有任何需要特别说的地方,重点还是在第一步的配置中:
- 我们首先定义了hystrix的异常处理实现类。
- 然后我们又扩展了feign提供的异常处理接口,并注入到Spring容器中,以让其参与feign的异常处理流程。
所以,本文所要解决的问题就是:
- 这两个自定义异常处理的扩展分别会在什么情况下触发?
- 以及两者触发的先后顺序是什么样的?
3. 源码解析
跟随以上这两个问题,在我们之前https://fulizhe.blog.csdn.net/article/details/127186374,https://fulizhe.blog.csdn.net/article/details/127189134基础上,看看以上两个配置项是如何生效的。
3.1 源码 - 初始化
根据之前的博客[https://fulizhe.blog.csdn.net/article/details/127186374](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();
}
}
}
3.2 源码 - 运行时
根据之前的博客[https://fulizhe.blog.csdn.net/article/details/127189134](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;
}
}
}
4. 总结
让我们来做个总结,忽略掉细节,形成全局认识。
端 | 依据 | 失败时的处理者 |
---|---|---|
服务端报错 | 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 自定义实现类 |