• Spring cloud Hystrix使用@HystrixCommand使用Hystrix组件及@EnableCircuitBreaker原理介绍


    通过@HystrixCommand注解实现在Spring Cloud使用Hystrix组件相关的工程

    • cloud-registration-center:注册中心
    • cloud-service-hystrix: 作为服务方的工程
    • cloud-consumer-hystrix:通过hystrix调用cloud-service-hystrix的接口

    1.cloud-service-hystrix

    作为服务方的工程,此工程比较简单,

    bootstrap-hystrix.yml

    只列出部分内容

    spring:
      application:
        # 本服务注册到注册到服务器的名称, 这个名称就是后面调用服务时的服务标识符
        name: cloud-hystrix-service
    ….
     

    2.SimpleCtl:提供服务的Control类

    @RestController
    public class SimpleCtl {
        private AtomicInteger count = new AtomicInteger();
        private AtomicInteger sleepCount = new AtomicInteger();
    
        @RequestMapping(value="/hystrix/simp le")
        public String hystrixClientCall(@RequestParam("time") long time){
            int newCount = count.incrementAndGet();
            return "time " + time + " hystrix" + newCount + ": " + ThreadLocalRandom.current().nextInt(1000);
        }
    ……
    }

    3.cloud-consumer-hystrix

    通过hystrix调用cloud-service-hystrix的接口

    pom.xml:只列出关键的jar
    
     <!-- hystrix -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-hystrix</artifactId>
        </dependency>

      bootstrap-hystrix-simple.yml属性配置

    # port
    server:
      port: 12083
    
    spring:
      application:
        # 本服务注册到注册到服务器的名称, 这个名称就是后面调用服务时的服务标识符
        name: cloud-consumer-hystrix
    eureka:
      client:
        serviceUrl:
          # 服务器注册/获取服务器的zone
          defaultZone: http://127.0.0.1:10761/eureka/
      instance:
        prefer-ip-address: true

    4.MyHystrixClient

    功能:通过RestTemplate调用服务的接口

    • @HystrixCommand:此注解表示此方法是hystrix方法,其中fallbackMethod定义回退方法的名称
    • String myFallback(long p, Throwable e) :HystrixCommand的回退方法,此方法必须和hystrix的执行方法在相同类中。可以把HystrixCommand的执行参数和执行失败的异常传入回退方法中
    @Service
    public class MyHystrixClient {
        @Autowired
        private RestTemplate restTemplate;
    
        @HystrixCommand(fallbackMethod = "myFallback")
        public String simpleHystrixClientCall(long time) {
            return restTemplate.getForEntity("http://CLOUD-HYSTRIX-SERVICE/hystrix/simple?time=" + time, String.class).getBody();
        }
    
        /**
         * 方法simpleHystrixClientCall的回退方法,可以指定将hystrix执行失败异常传入到方法中
         * @param p ystrix执行失败的传入方法的请求
         * @param e hystrix执行失败的异常对象
         * @return
         */
        String myFallback(long p, Throwable e) {
            return "Execute raw fallback: access service fail , req= " + p + " reason = " + e;
        }
    
    @HystrixCommand:其他参数说明
     public @interface HystrixCommand {
    
                // HystrixCommand 命令所属的组的名称:默认注解方法类的名称
                String groupKey() default "";
    
                // HystrixCommand 命令的key值,默认值为注解方法的名称
                String commandKey() default "";
    
                // 线程池名称,默认定义为groupKey
                String threadPoolKey() default "";
                // 定义回退方法的名称, 此方法必须和hystrix的执行方法在相同类中
                String fallbackMethod() default "";
                // 配置hystrix命令的参数
                HystrixProperty[] commandProperties() default {};
                // 配置hystrix依赖的线程池的参数
                 HystrixProperty[] threadPoolProperties() default {};
    
                // 如果hystrix方法抛出的异常包括RUNTIME_EXCEPTION,则会被封装HystrixRuntimeException异常。我们也可以通过此方法定义哪些需要忽略的异常
                Class<? extends Throwable>[] ignoreExceptions() default {};
    
                // 定义执行hystrix observable的命令的模式,类型详细见ObservableExecutionMode
                ObservableExecutionMode observableExecutionMode() default ObservableExecutionMode.EAGER;
    
                // 如果hystrix方法抛出的异常包括RUNTIME_EXCEPTION,则会被封装HystrixRuntimeException异常。此方法定义需要抛出的异常
                HystrixException[] raiseHystrixExceptions() default {};
    
                // 定义回调方法:但是defaultFallback不能传入参数,返回参数和hystrix的命令兼容
                String defaultFallback() default "";
            }

    SimpleCtl

    通过Control的方法调用MyHystrixClient的方法,返回执行结果

    @RestController
    public class SimpleCtl {
        @Autowired
        private MyHystrixClient myHystrixClient;
    
        @RequestMapping(value="/hystrix/simple")
        public String simpleClientCall(){
            return "rsp: " + myHystrixClient.simpleHystrixClientCall(System.currentTimeMillis());
        }
    
    }
     

    配置Hystrix的自定义参数

    
    

    以下时配置hystrix的线程池的大小,其他的配置见Spring cloud系列九 Hystrix的配置属性优先级和详解

    
    

    application-hystrix-simple.yml

    
    
    # 配置hystrix的参数
    hystrix:
      threadpool:
        # default: 默认参数,作用的所有的hystrix的客户端
        default:
          coreSize: 10

     启动类HystrixSimpleCloudConsumerApplication

    • @EnableCircuitBreaker :启动断路器
    • 方法RestTemplate restTemplate():初始化RestTemplate 对象,并使用 @LoadBalanced作负载均衡
    @SpringBootApplication
    @EnableCircuitBreaker 
    @EnableEurekaClient // 配置本应用将使用服务注册和服务发现
    public class HystrixSimpleCloudConsumerApplication {
    
        public static void main(String[] args) {
            args = new String[1];
            args[0] = "--spring.profiles.active=hystrix-simple";
            SpringApplication.run(HystrixSimpleCloudConsumerApplication.class, args);
        }
    
        /**
         * 初始RestTemplate
         * @return
         */
        @LoadBalanced
        @Bean
        public RestTemplate restTemplate() {
            return new RestTemplate();
        }
    
        /**
         * 使用fastjson做为json的解析器
         * @return
         */
        @Bean
        public HttpMessageConverters fastJsonHttpMessageConverters() {
            FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
            FastJsonConfig fastJsonConfig = new FastJsonConfig();
            fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat);
            fastConverter.setFastJsonConfig(fastJsonConfig);
            HttpMessageConverter<?> converter = fastConverter;
            return new HttpMessageConverters(converter);
        }
    }

    6.测试

    启动服务

    启动工程cloud-registration-center:配置中心地址:http://127.0.0.1:10761 
    启动工程cloud-service-hystrix的HystrixCloudServiceApplication的启动类 
    启动工程cloud-consumer-hystrix的HystrixSimpleCloudConsumerApplication的启动类

    测试一 
    执行cloud-consumer-hystrix的调用cloud-service-hystrix服务的接口 
    在浏览器中运行URL: http://127.0.0.1:12083//hystrix/simple 
    返回:

    "rsp: "time 1511015252093 hystrix1: 434""

    测试二 
    停止cloud-service-hystrix服务 
    执行cloud-consumer-hystrix的调用cloud-service-hystrix服务的接口 
    在浏览器中运行URL: http://127.0.0.1:12083//hystrix/simple 
    返回:

    "rsp: Execute raw fallback: access service fail , req= 1511103681411 reason = com.netflix.hystrix.exception.HystrixTimeoutException"

     说明fallback方法被执行了

    测试三 
    修改application-hystrix-simple.yml 中的hystrix的线程池的线程数量为0,

    # 配置hystrix的参数
    hystrix:
      threadpool:
        # default: 默认参数,作用的所有的hystrix的客户端
        default:
          coreSize: 0

    重启cloud-service-hystrix和cloud-consumer-hystrix, 
    执行http://127.0.0.1:12083//hystrix/simple 
    提示执行hystrix方法失败,说明我们的配置启作用了

    @EnableCircuitBreaker的原理

    在启动类HystrixSimpleCloudConsumerApplication中使用@EnableCircuitBreaker + @HystrixCommand 注解启动Hystrix断路器的功能。本节介绍此注解的原理

    7.1 HystrixCommandAspect

    HystrixCommandAspect 通过AOP拦截所有的@HystrixCommand注解的方法,从而使得@HystrixCommand能够集成到Spring boot中

    HystrixCommandAspect的关键代码如下:

    • 1 方法 hystrixCommandAnnotationPointcut() 定义拦截注解HystrixCommand
    • 2 方法 hystrixCollapserAnnotationPointcut()定义拦截注解HystrixCollapser
    • 3 方法methodsAnnotatedWithHystrixCommand(…)通过@Around(…)拦截所有HystrixCommand和HystrixCollapser注解的方法。详细见方法注解
    @Aspect
    public class HystrixCommandAspect {
    
        @Pointcut("@annotation(com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand)")
        public void hystrixCommandAnnotationPointcut() {
        }
    
        @Pointcut("@annotation(com.netflix.hystrix.contrib.javanica.annotation.HystrixCollapser)")
        public void hystrixCollapserAnnotationPointcut() {
        }
    
      @Around("hystrixCommandAnnotationPointcut() || hystrixCollapserAnnotationPointcut()")
        public Object methodsAnnotatedWithHystrixCommand(final ProceedingJoinPoint joinPoint) throws Throwable {
            // 获取拦截的Method
            Method method = getMethodFromTarget(joinPoint);
            Validate.notNull(method, "failed to get method from joinPoint: %s", joinPoint);
            // 只有被HystrixCommand和HystrixCollapser注解的方法才执行后续操作
            if (method.isAnnotationPresent(HystrixCommand.class) && method.isAnnotationPresent(HystrixCollapser.class)) {
                throw new IllegalStateException("method cannot be annotated with HystrixCommand and HystrixCollapser " +
                        "annotations at the same time");
            }
            // 根据拦截方法的注解HystrixCommand或HystrixCollapser分别获取CommandMetaHolderFactory或者CollapserMetaHolderFactory类
            MetaHolderFactory metaHolderFactory = META_HOLDER_FACTORY_MAP.get(HystrixPointcutType.of(method));
            // 将拦截方法封装到MetaHolder中
            MetaHolder metaHolder = metaHolderFactory.create(joinPoint);
            // 根据metaHolder生成相应的HystrixCommand,包含生成hystrix执行时需要的配置信息,这些配置信息来自默认配置或我们自定义的属性
            HystrixInvokable invokable = HystrixCommandFactory.getInstance().create(metaHolder);
            ExecutionType executionType = metaHolder.isCollapserAnnotationPresent() ?
                    metaHolder.getCollapserExecutionType() : metaHolder.getExecutionType();
    
            Object result;
            try {
               // 根据是否是Observable执行CommandExecutor.execute()方法,executeObservable最后也会执行CommandExecutor.execute()方法
                if (!metaHolder.isObservable()) {
                    result = CommandExecutor.execute(invokable, executionType, metaHolder);
                } else {
                    result = executeObservable(invokable, executionType, metaHolder);
                }
            } catch (HystrixBadRequestException e) {
                throw e.getCause() != null ? e.getCause() : e;
            } catch (HystrixRuntimeException e) {
                throw hystrixRuntimeExceptionToThrowable(metaHolder, e);
            }
            return result;
        }
    
    ….
    }

    @EnableCircuitBreaker 和 EnableCircuitBreakerImportSelector

    那么谁来触发HystrixCircuitBreakerConfiguration执行初始化

    先看spring-cloud-netflix-core**.jar包的spring.factories里有这段配置,是由注解EnableCircuitBreaker触发的

    org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker=
    org.springframework.cloud.netflix.hystrix.HystrixCircuitBreakerConfiguration
    •  

    那么@EnableCircuitBreaker如何触发HystrixCircuitBreakerConfiguration 
    通过源码查看,此类通过@Import初始化EnableCircuitBreakerImportSelector类

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @Import(EnableCircuitBreakerImportSelector.class)
    public @interface EnableCircuitBreaker {
    
    }

    EnableCircuitBreakerImportSelector是SpringFactoryImportSelector子类。此类在初始化后,会执行selectImports(AnnotationMetadata metadata)的方法。此方法会根据注解启动的注解(这里指@EnableCircuitBreaker)从spring.factories文件中获取其配置需要初始化@Configuration类(这里是org.springframework.cloud.netflix.hystrix.HystrixCircuitBreakerConfiguration),从而最终初始化HystrixCommandAspect 类,从而实现拦截HystrixCommand的功能

    以上就是通过@EnableCircuitBreake可以开启Hystrix的原理

  • 相关阅读:
    [记录]Python2.7使用argparse模块
    [记录]MySQL读写分离(Atlas和MySQL-proxy)
    [记录]Shell中的getopts和getopt用法
    [记录]CentOS搭建SVN服务器(主从同步)
    [记录]Zabbix3.4配置监控Oracle12c的存活状态和表空间使用率
    [记录]一则清理MySQL大表以释放磁盘空间的案例
    [原创]Oracle 12c的备份和恢复策略
    Linux awk用法
    Oracle数据库学习笔记
    oracle无法删除当前连接用户方法
  • 原文地址:https://www.cnblogs.com/duan2/p/9307217.html
Copyright © 2020-2023  润新知