zoukankan      html  css  js  c++  java
  • Hystrix 异常处理机制

    Hystrix错误类型

    https://github.com/Netflix/Hystrix/wiki/How-To-Use#ErrorPropagation

    Failure TypeException classException.causesubject 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;
            }
        }
    
    • WebApplicationExceptionjavax.ws.rs 包中异常,通过 Jersey 抛出该异常能够将返回的 HttpCode 封装进该异常中(上述代码中展示了如何封装 HttpCode),抛出该异常,调用端就能得到返回的 HttpCode。



    链接:https://www.jianshu.com/p/f240ca7bb7c0
  • 相关阅读:
    获取项目中.txt 文件的内容
    jdbc 连接各种数据库
    修改Eclipse中项目在Apache Tomcat中的部署路径
    java学习之继承之子父类中变量的特点
    java学习之继承、多态
    java学习之单例模式(饿汉式与懒汉式)
    java学习之对象初始化过程
    java学习之静态块、显示初始化块、构造函数、this在构造函数中的调用,区别联系与调用顺序
    java学习之数组的两种常见错误
    java学习之堆与栈的区别
  • 原文地址:https://www.cnblogs.com/duanxz/p/4516425.html
Copyright © 2011-2022 走看看