Hystrix错误类型
https://github.com/Netflix/Hystrix/wiki/How-To-Use#ErrorPropagation
Failure Type | Exception class | Exception.cause | subject to fallback |
---|---|---|---|
FAILURE | HystrixRuntimeException |
underlying exception (user-controlled) | YES |
TIMEOUT | HystrixRuntimeException |
j.u.c.TimeoutException |
YES |
SHORT_CIRCUITED | HystrixRuntimeException |
j.l.RuntimeException |
YES |
THREAD_POOL_REJECTED | HystrixRuntimeException |
j.u.c.RejectedExecutionException |
YES |
SEMAPHORE_REJECTED | HystrixRuntimeException |
j.l.RuntimeException |
YES |
BAD_REQUEST | HystrixBadRequestException |
underlying exception (user-controlled) | NO |
Hystrix 的异常处理中,有5种出错的情况下会被 fallback 所截获,从而触发 fallback,这些情况是:
- FAILURE:执行失败,抛出异常。
- TIMEOUT:执行超时。
- SHORT_CIRCUITED:断路器打开。
- THREAD_POOL_REJECTED:线程池拒绝。
- SEMAPHORE_REJECTED:信号量拒绝。
有一种类型的异常是不会触发 fallback 且不会被计数进入熔断的,它是 BAD_REQUEST,会抛出 HystrixBadRequestException,这种异常一般对应的是由非法参数或者一些非系统异常引起的,对于这类异常可以根据响应创建对应的异常进行异常封装或者直接处理。
示例1:
下图是手动抛一个 HystrixBadRequestException 异常进行测试。
可以看到,并没有进行熔断。
示例2:
/** * HystrixBadRequestException: * 与由HystrixCommand抛出的所有其他异常不同,这不会触发回退,也不会对失败度量进行计数,因此不会触发断路器。 * @return */ @HystrixCommand(fallbackMethod="helloBackMethodFirst",ignoreExceptions=HystrixBadRequestException.class) public String helloService() { logger.info("start invoke service"); throw new HystrixBadRequestException("consumer exception"); } public String helloBackMethodFirst(Throwable e) { // 一些异常判断 if (e instanceof RuntimeException) { logger.info("error"); } if (e instanceof IllegalStateException) { } return "error2:" + e.getMessage(); }
结果:error2:consumer exception
错误信息获取
错误信息的获取非常容易,只需要在回滚方法中加入 Throwable 参数即可:
使用 Hystrix 解决内部调用抛出异常问题
源码分析
- Hystrix 的设计方案是通过命令模式加 RxJava 实现的观察者模式来开发的,想完全熟悉 Hystrix 的运作流程需要熟练掌握 RxJava,本文只对源码进行简单介绍,后面有时间有机会再详细介绍
- Hystrix如何处理异常的:
代码位置:com.netflix.hystrix.AbstractCommand#executeCommandAndObserve
//省略部分代码 private Observable<R> executeCommandAndObserve(final AbstractCommand<R> _cmd) { //省略部分代码 final Func1<Throwable, Observable<R>> handleFallback = new Func1<Throwable, Observable<R>>() { @Override public Observable<R> call(Throwable t) { Exception e = getExceptionFromThrowable(t); executionResult = executionResult.setExecutionException(e); if (e instanceof RejectedExecutionException) { return handleThreadPoolRejectionViaFallback(e); } else if (t instanceof HystrixTimeoutException) { return handleTimeoutViaFallback(); } else if (t instanceof HystrixBadRequestException) { return handleBadRequestByEmittingError(e); } else { /* * Treat HystrixBadRequestException from ExecutionHook like a plain HystrixBadRequestException. */ if (e instanceof HystrixBadRequestException) { eventNotifier.markEvent(HystrixEventType.BAD_REQUEST, commandKey); return Observable.error(e); } return handleFailureViaFallback(e); } } }; //省略部分代码 }
该类中该方法为发生异常的回调方法,由此可以看出如果抛出异常如果是 HystrixBadRequestException
是直接处理异常之后进行抛出(这里不会触发熔断机制),而不是进入回调方法。
解决方案
- 那么我们对于请求异常的解决方案就需要通过
HystrixBadRequestException
来解决了(不会触发熔断机制),根据返回响应创建对应异常并将异常封装进HystrixBadRequestException
,业务系统调用中取出HystrixBadRequestException
中的自定义异常进行处理,
封装异常说明:
public class UserErrorDecoder implements ErrorDecoder{
private Logger logger = LoggerFactory.getLogger(getClass());
public Exception decode(String methodKey, Response response) {
ObjectMapper om = new JiaJianJacksonObjectMapper();
JiaJianResponse resEntity;
Exception exception = null;
try {
resEntity = om.readValue(Util.toString(response.body().asReader()), JiaJianResponse.class);
//为了说明我使用的 WebApplicationException 基类,去掉了封装
exception = new WebApplicationException(javax.ws.rs.core.Response.status(response.status()).entity(resEntity).type(MediaType.APPLICATION_JSON).build());
// 这里只封装4开头的请求异常
if (400 <= response.status() && response.status() < 500){
exception = new HystrixBadRequestException("request exception wrapper", exception);
}else{
logger.error(exception.getMessage(), exception);
}
} catch (IOException ex) {
logger.error(ex.getMessage(), ex);
}
return exception;
}
}
为 Feign 配置 ErrorDecoder
@Configuration
public class FeignConfiguration {
@Bean
public ErrorDecoder errorDecoder(){
return new UserErrorDecoder();
}
}
业务系统处理异常说明:
@Override
public UserSigninResEntity signIn(UserSigninReqEntity param) throws Exception {
try {
//省略部分代码
UserSigninResEntity entity = userRemoteCall.signin(secretConfiguration.getKeys().get("user-service"), param);
//省略部分代码
} catch (Exception ex) {
//这里进行异常处理
if(ex.getCause() instanceof WebApplicationException){
ex = (WebApplicationException) ex.getCause();
}
logger.error(ex.getMessage(), ex);
throw ex;
}
}
WebApplicationException
是javax.ws.rs
包中异常,通过 Jersey 抛出该异常能够将返回的 HttpCode 封装进该异常中(上述代码中展示了如何封装 HttpCode),抛出该异常,调用端就能得到返回的 HttpCode。
链接:https://www.jianshu.com/p/f240ca7bb7c0