Hystrix 异常处理机制--待整理

 

一、Hystrix错误类型及什么情况下会触发fallback方法?

二、fallback方法在什么情况下会抛出异常

一、Hystrix错误类型及什么情况下会触发fallback方法?

结果类型
Exception classException.cause描述subject to fallback
EMIT     值传递 NO
SUCCESS     执行完成,没有错误 NO
FAILURE HystrixRuntimeException underlying exception (user-controlled) 执行抛出异常 YES
TIMEOUT HystrixRuntimeException j.u.c.TimeoutException 执行开始,但没有在允许的时间内完成 YES
BAD_REQUEST HystrixBadRequestException underlying exception (user-controlled) 执行抛出HystrixBadRequestException NO
SHORT_CIRCUITED HystrixRuntimeException j.l.RuntimeException 断路器打开,不尝试执行 YES
THREAD_POOL_REJECTED HystrixRuntimeException j.u.c.RejectedExecutionException 线程池拒绝,不尝试执行 YES
SEMAPHORE_REJECTED HystrixRuntimeException j.l.RuntimeException 信号量拒绝,不尝试执行 YES
        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;
        }
    }
  • WebApplicationExceptionjavax.ws.rs 包中异常,通过 Jersey 抛出该异常能够将返回的 HttpCode 封装进该异常中(上述代码中展示了如何封装 HttpCode),抛出该异常,调用端就能得到返回的 HttpCode。

二、fallback方法在什么情况下会抛出异常

 

名字

描述

抛异常

FALLBACK_EMIT

Fallback值传递

NO

FALLBACK_SUCCESS

Fallback执行完成,没有错误

NO

FALLBACK_FAILURE

Fallback执行抛出出错

YES

FALLBACK_REJECTED

Fallback信号量拒绝,不尝试执行

YES

FALLBACK_MISSING

没有Fallback实例

YES

 

三、hystrix dashboard界面监控参数

hystrix错误场景

1、com.netflix.hystrix.exception.HystrixRuntimeException,Caused by: java.util.concurrent.TimeoutException: null

熔断原因:调用的下游服务超时。

处理办法:配置超时有Hystrix、Feign、ribbon等

ribbon.ReadTimeout

Spring Cloud中Hystrix、Ribbon及Feign的熔断关系是什么? 超时说明

2、com.netflix.hystrix.exception.HystrixRuntimeException:xxx#xxx(String,String)could not be queued for execution and no fallback available. 

Caused by: java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@35ad9082 rejected from java.util.concurrent.ThreadPoolExecutor@737d0c7f[Running, pool size = 100, active threads = 100, queued tasks = 0, completed tasks = 855874] at java.util.concurren

或者

Rejected command because thread-pool queueSize is at rejection threshold

熔断原因:超过Hystrix配置的线程数量(Hystrix默认是10个线程),超过就会报这个异常

处理办法:

hystrix:
  threadpool:
    default:
      coreSize: 200 ##并发执行的最大线程数,默认10
      maxQueueSize: 200 ##BlockingQueue的最大队列数
      queueSizeRejectionThreshold: 50 ##即使maxQueueSize没有达到,达到queueSizeRejectionThreshold该值后,请求也会被拒绝
    default:
      execution:
        timeout:
          enabled: true
        isolation:
          strategy: THREAD
          semaphore:
            maxConcurrentRequests: 1000
          thread:
            timeoutInMilliseconds: 30000
queueSizeRejectionThreshold:(排队线程数量阈值,默认为5,达到时拒绝,如果配置了该选项,队列的大小是该队列)注意:如果maxQueueSize=-1的话,则该选项不起作用。
参考《服务容错保护断路器Hystrix之五:配置

3、com.netflix.hystrix.exception.HystrixRuntimeException: xxx#xxx(String,String) timed-out and no fallback available.


链接:https://www.jianshu.com/p/f240ca7bb7c0
posted on 2015-05-20 10:06  duanxz  阅读(3644)  评论(0编辑  收藏  举报