feign注册
spring加载的时候通过@EnableFeignClients的FeignClientsRegistrar注册扫描所以得FeignClient以及Configuration,最终注册为ReflectiveFeign,最终通过代理类FeignInvocationHandler实现方法的调用,在
FeignInvocationHandler中通过SynchronousMethodHandler方法执行实际逻辑
当调用Feignclient里面的方法的时候最终会知道SynchronousMethodHandler的invoke方法
这里主要总结下feign的失败重试逻辑与异常捕获情况
feign异常
在SynchronousMethodHandler方法中会首先调用executeAndDecode方法,可以看到调用client.execute请求方法之后如果调用失败了则会执行errorExecuting直接返回RetryableException,
还有一个重要的方法是asyncResponseHandler.handleResponse,到这一步表示HTTP请求成功了,这里面将会根据响应码以及FeignClient中的方法返回类型反序列化来处理响应结果
首先判断是否为内定的Response类型,如果不是会根据项目配置的Decoder,可以根据自己需要配置fastjson或者jackjson以及其他的序列号工具
@Beanpublic Decoder feignDecoder() {MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();List<MediaType> supportedMediaTypes = new ArrayList<>();supportedMediaTypes.add(MediaType.ALL);converter.setSupportedMediaTypes(supportedMediaTypes);ObjectFactory<HttpMessageConverters> objectFactory = () -> new HttpMessageConverters(converter);return new ResponseEntityDecoder(new SpringDecoder(objectFactory));}
如果是其他响应码会直接调用ErrorDecoder接口的decode()方法,默认的errorDecoder为ErrorDecoder.Default,默认返回FeignExcpetion,如果响应头当中含有Retry-After,则会返回RetryableException异常信息,进行后续逻辑来判断是否需要重试,但是这个默认的比较鸡肋,需要响应端提前在响应头加这个字段,调用第三方的接口时无法完成。
所以可以自己定义ErrorDecoder实现定制化的功能,比如只有当响应码为500的时候返回RetryableException后续进行重试
@Beanpublic ErrorDecoder errorDecoder() {return (methodKey, response) -> {FeignException exception = errorStatus(methodKey, response);if (response.status() >= 500 && response.status() <= 599) {exception = new RetryableException(response.status(),exception.getMessage(),response.request().httpMethod(),exception,null,response.request());}return exception;};}
也可以自己根据响应码指定异常信息:
public class CustomErrorDecoder implements ErrorDecoder {@Overridepublic Exception decode(String methodKey, Response response) {int status = response.status();switch (status) {case 400:return new CustomFeignException(status, "Bad Request");case 401:return new CustomFeignException(status, "Unauthorized");case 403:return new CustomFeignException(status, "Forbidden");case 404:return new CustomFeignException(status, "Not Found");case 500:return new CustomFeignException(status, "Internal Server Error");default:return new Exception(response.reason());}}
}
feign重试
如果前面步骤返回了RetryableException,则会调用Rtryer的continueOrPropagate方法,
默认为不重试,Reteyer里面内部类定义了一个Default重试方法,通过最大次数以及重试间隔来让线程休眠一段时间达到重试,在这期间调用FeignClient的线程一直处于阻塞中,所以重试不能间隔太长时间,这期间如果服务重启重试的线程则会直接断掉,所以如果不是页面操作调用接口的可以直接异步线程调用FeignClient的方法,或者自定义重试逻辑,存入延迟队列或者借助mq来处理调用失败的接口,
@Beanpublic Retryer retryer() {return new CustomRetryer(3, 1000); // 最多重试3次,每次间隔1秒}
ErrorDecode只有在请求成功的时候才会调用,如果连接超时或者网络错误,直接会抛出RetryableException,不会调用decode,所以不适合做全局异常处理