本文主要包括如下内容:
注册中心,此工程之前已经介绍过了,这里略
作为服务方的工程,此工程比较简单,详细的创建方法见这节 Spring cloud系列一 包含所有基本要素的完整Spring Cloud demo
其他和例子有关的配置如下:
只列出部分内容
spring: application: # 本服务注册到注册到服务器的名称, 这个名称就是后面调用服务时的服务标识符 name: cloud-hystrix-service ….
@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); } …… }
通过hystrix调用cloud-service-hystrix的接口
<!-- hystrix --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> </dependency>
# 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
功能:通过RestTemplate调用服务的接口
@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; } }
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 ""; }
通过Control的方法调用MyHystrixClient的方法,返回执行结果
@RestController public class SimpleCtl { @Autowired private MyHystrixClient myHystrixClient; @RequestMapping(value="/hystrix/simple") public String simpleClientCall(){ return "rsp: " + myHystrixClient.simpleHystrixClientCall(System.currentTimeMillis()); } }
以下时配置hystrix的线程池的大小,其他的配置见 Spring cloud系列九 Hystrix的配置属性优先级和详解
application-hystrix-simple.yml
# 配置hystrix的参数 hystrix: threadpool: # default: 默认参数,作用的所有的hystrix的客户端 default: coreSize: 10
@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); } }
启动工程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方法失败,说明我们的配置启作用了
在启动类HystrixSimpleCloudConsumerApplication中使用@EnableCircuitBreaker + @HystrixCommand 注解启动Hystrix断路器的功能。本节介绍此注解的原理
HystrixCommandAspect 通过AOP拦截所有的@HystrixCommand注解的方法,从而使得@HystrixCommand能够集成到Spring boot中
HystrixCommandAspect的关键代码如下:
@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; } …. }
那么HystrixCommandAspect是如何初始化,是通过HystrixCircuitBreakerConfiguration实现的
@Configuration public class HystrixCircuitBreakerConfiguration { @Bean public HystrixCommandAspect hystrixCommandAspect() { return new HystrixCommandAspect(); } …. }
那么谁来触发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的原理
以上的详细的代码见下面
github代码,请尽量使用tag v0.7,不要使用master,因为我不能保证master代码一直不变