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以及其他的序列號工具
@Bean
public 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後續進行重試
@Bean
public 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 {
@Override
public 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來處理呼叫失敗的介面,
@Bean
public Retryer retryer() {
return new CustomRetryer(3, 1000); // 最多重試3次,每次間隔1秒
}
ErrorDecode只有在請求成功的時候才會呼叫,如果連線超時或者網路錯誤,直接會丟擲RetryableException,不會呼叫decode,所以不適合做全域性異常處理