Spring cloud系列十 使用@HystrixCommand使用Hystrix组件及@EnableCircuitBreaker原理介绍

1. 概述

本文主要包括如下内容:

  • 1 通过@HystrixCommand注解实现在Spring Cloud中使用Hystrix组件:包括通过@HystrixCommand调用服务,实现fallback方法
  • 2 如何通过配置自定义的Hystrix参数
  • 3 对@HystrixCommand+@EnableCircuitBreaker的原理进行介绍

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

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

3. cloud-registration-center

注册中心,此工程之前已经介绍过了,这里略

4. cloud-service-hystrix

作为服务方的工程,此工程比较简单,详细的创建方法见这节Spring cloud系列一 包含所有基本要素的完整Spring Cloud demo
其他和例子有关的配置如下:

4.1. bootstrap-hystrix.yml

只列出部分内容

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

4.2. SimpleCtl:提供服务的Control类

@RestController
public class SimpleCtl {
    private AtomicInteger count = new AtomicInteger();
    private AtomicInteger sleepCount = new AtomicInteger();

    @RequestMapping(value="/hystrix/simple")
    public String hystrixClientCall(@RequestParam("time") long time){
        int newCount = count.incrementAndGet();
        return "time " + time + " hystrix" + newCount + ": " + ThreadLocalRandom.current().nextInt(1000);
    }
……
}

5. cloud-consumer-hystrix

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

5.1. pom.xml:只列出关键的jar

 
    <dependency>
        <groupId>org.springframework.cloudgroupId>
        <artifactId>spring-cloud-starter-hystrixartifactId>
    dependency>

5.2. 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

5.3. 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异常。我们也可以通过此方法定义哪些需要忽略的异常
            Classextends Throwable>[] ignoreExceptions() default {};

            // 定义执行hystrix observable的命令的模式,类型详细见ObservableExecutionMode
            ObservableExecutionMode observableExecutionMode() default ObservableExecutionMode.EAGER;

            // 如果hystrix方法抛出的异常包括RUNTIME_EXCEPTION,则会被封装HystrixRuntimeException异常。此方法定义需要抛出的异常
            HystrixException[] raiseHystrixExceptions() default {};

            // 定义回调方法:但是defaultFallback不能传入参数,返回参数和hystrix的命令兼容
            String defaultFallback() default "";
        }

5.4. SimpleCtl

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

@RestController
public class SimpleCtl {
    @Autowired
    private MyHystrixClient myHystrixClient;

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

}

5.5. 配置Hystrix的自定义参数

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

application-hystrix-simple.yml

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

5.6. 启动类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方法失败,说明我们的配置启作用了

7. @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;
    }

….
}

7.2. HystrixCircuitBreakerConfiguration

那么HystrixCommandAspect是如何初始化,是通过HystrixCircuitBreakerConfiguration实现的

@Configuration
public class HystrixCircuitBreakerConfiguration {
@Bean
    public HystrixCommandAspect hystrixCommandAspect() {
        return new HystrixCommandAspect();
    }
….
}

7.3. @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的原理

8. 代码

以上的详细的代码见下面
github代码,请尽量使用tag v0.7,不要使用master,因为我不能保证master代码一直不变

你可能感兴趣的:(spring-cloud,Spring,Cloud使用总结)