序
使用feign client進(jìn)行restful服務(wù)間的調(diào)用,除了要注意超時時間账锹、retry的設(shè)置外,還有一個關(guān)于自定義異常的部分坷襟,需要注意一下奸柬,不然容易出錯。
nginx對于upstream的health check
Nginx 默認(rèn)判斷失敗節(jié)點(diǎn)狀態(tài)以connect refuse和time out狀態(tài)為準(zhǔn)婴程,不以HTTP錯誤狀態(tài)進(jìn)行判斷失敗廓奕,因為HTTP只要能返回狀態(tài)說明該節(jié)點(diǎn)還可以正常連接,所以nginx判斷其還是存活狀態(tài)档叔;除非添加了proxy_next_upstream指令設(shè)置對404桌粉、502、503衙四、504铃肯、500和time out等錯誤進(jìn)行轉(zhuǎn)到備機(jī)處理。
feign及hystrix對于服務(wù)提供方的health check
HystrixBadRequestException
這個異常主要是用來適配IllegalArgumentException這類異常传蹈。HystrixBadRequestException與其他HystrixCommand拋出的異常不同押逼,該異常不會納入circuit breaker的統(tǒng)計里頭,即不會觸發(fā)熔斷卡睦。
feign client對restful調(diào)用的異常處理
/Users/xixicat/.m2/repository/io/github/openfeign/feign-core/9.3.1/feign-core-9.3.1-sources.jar!/feign/SynchronousMethodHandler.java
Object executeAndDecode(RequestTemplate template) 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 10
response.toBuilder().request(request).build();
} catch (IOException e) {
if (logLevel != Logger.Level.NONE) {
logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime(start));
}
throw errorExecuting(request, e);
}
long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
boolean shouldClose = true;
try {
if (logLevel != Logger.Level.NONE) {
response =
logger.logAndRebufferResponse(metadata.configKey(), logLevel, response, elapsedTime);
// ensure the request is set. TODO: remove in Feign 10
response.toBuilder().request(request).build();
}
if (Response.class == metadata.returnType()) {
if (response.body() == null) {
return response;
}
if (response.body().length() == null ||
response.body().length() > MAX_RESPONSE_BUFFER_SIZE) {
shouldClose = false;
return response;
}
// Ensure the response body is disconnected
byte[] bodyData = Util.toByteArray(response.body().asInputStream());
return response.toBuilder().body(bodyData).build();
}
if (response.status() >= 200 && response.status() < 300) {
if (void.class == metadata.returnType()) {
return null;
} else {
return decode(response);
}
} else if (decode404 && response.status() == 404) {
return decoder.decode(response, metadata.returnType());
} else {
throw errorDecoder.decode(metadata.configKey(), response);
}
} catch (IOException e) {
if (logLevel != Logger.Level.NONE) {
logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime);
}
throw errorReading(request, response, e);
} finally {
if (shouldClose) {
ensureClosed(response.body());
}
}
}
其中對status code的處理見這段
if (response.status() >= 200 && response.status() < 300) {
if (void.class == metadata.returnType()) {
return null;
} else {
return decode(response);
}
} else if (decode404 && response.status() == 404) {
return decoder.decode(response, metadata.returnType());
} else {
throw errorDecoder.decode(metadata.configKey(), response);
}
也就是feign client的處理跟nginx的是不一樣的宴胧,feign client把非200的以及404(
可以配置是否納入異常
)都算成error,都轉(zhuǎn)給errorDecoder去處理了表锻。
小結(jié)
要特別注意恕齐,對于restful拋出的4xx的錯誤,也許大部分是業(yè)務(wù)異常,并不是服務(wù)提供方的異常显歧,因此在進(jìn)行feign client調(diào)用的時候仪或,需要進(jìn)行errorDecoder去處理,適配為HystrixBadRequestException士骤,好避開circuit breaker的統(tǒng)計范删,否則就容易誤判,傳幾個錯誤的參數(shù)拷肌,立馬就熔斷整個服務(wù)了到旦,后果不堪設(shè)想。
- 附errorDecoder實(shí)例
@Configuration
public class BizExceptionFeignErrorDecoder implements feign.codec.ErrorDecoder{
private static final Logger logger = LoggerFactory.getLogger(BizExceptionFeignErrorDecoder.class);
@Override
public Exception decode(String methodKey, Response response) {
if(response.status() >= 400 && response.status() <= 499){
return new HystrixBadRequestException("xxxxxx");
}
return feign.FeignException.errorStatus(methodKey, response);
}
}