当前位置:   article > 正文

Spring Cloud Netflix Hystrix-初识篇_org.springframework.cloud:spring-cloud-starter-net

org.springframework.cloud:spring-cloud-starter-netflix-hystrix

关键词:服务短路    服务熔断    服务降级    Hystrix  


最近工作上面比较忙,学习稍微耽搁了。现在让我们继续学习Spring Cloud系列之Hystrix


█ 使用

环境准备:

Hystrix作为服务熔断与降级的工具,既可以用在服务提供者方,也可以用在服务调用者方。本文例子是在服务调用方使用Hystrix。

1、搭建服务端,用于提供服务:

创建Controller,调用接口为:/server/hello。以8080作为服务端口启动服务。

  1. @RestController
  2. @RequestMapping("/server")
  3. public class Server8080Controller {
  4. private Random random = new Random(10);
  5. @GetMapping("/hello")
  6. public String sayHello() throws Exception{
  7. // 当产生的随机数大于5,就抛出异常,否则正常返回值
  8. if (random.nextInt() > 5) {
  9. throw new IllegalAccessException("服务端发生异常");
  10. }
  11. return "server hello world";
  12. }
  13. }

2、搭建客户端,用于调用服务端接口:

(1)引入Spring Cloud Netflix Hystrix依赖

  1. <dependency>
  2. <groupId>org.springframework.cloud</groupId>
  3. <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
  4. </dependency>

(2)创建Controller

  1. @RestController
  2. @RequestMapping("/client")
  3. public class ClientController {
  4. private RestTemplate restTemplate = new RestTemplate();
  5. @GetMapping("/hello")
  6. public String sayHello() throws Exception{
  7. return null;
  8. }
  9. }

使用Hystrix:

如何使用Hystrix功能,有两种方式,编码式声明式

1、编码式

(1)通过继承HystrixCommand(注意是抽象类com.netflix.hystrix.HystrixCommand),实现run()方法,重写getFallback()方法:

  1. public class CustomHystrixCommand extends HystrixCommand<String> {
  2. private final RestTemplate restTemplate;
  3. // 因为HystrixCommand中没有提供无参构造器,所以子类要显示调用super有参构造器
  4. // 默认的无参构造器会默认调用父类的super无参构造器
  5. public CustomHystrixCommand(String groupName, RestTemplate restTemplate) {
  6. super(HystrixCommandGroupKey.Factory.asKey(groupName));
  7. this.restTemplate = restTemplate;
  8. }
  9. @Override
  10. protected String run() throws Exception {
  11. return restTemplate.getForObject("http://localhost:8080/server/hello", String.class);
  12. }
  13. @Override
  14. protected String getFallback() {
  15. return "CustomHystrixCommandImpl getFallback";
  16. }
  17. }

在run方法中,通过RestTemplate调用服务端接口,当run方法正确执行,可获取到服务端返回值。当发生异常时,会进行熔断,调用getFallback方法,将getFallback方法的返回值返回给调用者。通过HystrixCommand<String>中使用泛型,来标注方法返回值类型,不使用泛型,则为Object类型。

(2)修改客户端ClientController,使用CustomHystrixCommand:

  1. @RestController
  2. @RequestMapping("/client")
  3. public class ClientController {
  4. private RestTemplate restTemplate = new RestTemplate();
  5. @GetMapping("/hello")
  6. public String sayHello() throws Exception{
  7. CustomHystrixCommand customHystrixCommand = new CustomHystrixCommand("customHystrixCommand", restTemplate);
  8. return customHystrixCommand.execute();
  9. }
  10. }

通过调用HystrixCommand的execute()方法,最终会调用到run()方法。这种方式比较像线程Thread的API。

(3)Hystrix也提供了响应式编程模型,此时可结合使用Spring WebFlux进行服务端接口调用。通过继承HystrixObservableCommand实现响应式编程:

  1. public class CustomHystrixObservableCommand extends HystrixObservableCommand {
  2. public CustomHystrixObservableCommand(String groupName) {
  3. super(HystrixCommandGroupKey.Factory.asKey(groupName));
  4. }
  5. @Override
  6. protected Observable construct() {
  7. return Observable.just("在这里可以使用Spring WebFlux进行服务端调用");
  8. }
  9. @Override
  10. protected Observable resumeWithFallback() {
  11. return Observable.just("回退方法");
  12. }
  13. }

2、声明式

声明式方式是在方法上标注注解@HystrixCommand,通过属性fallbackMethod配置熔断后调用的回退方法。

(1)修改客户端ClientController:

  1. @RestController
  2. @RequestMapping("/rest/client")
  3. public class ClientController {
  4. @Autowired
  5. private RestTemplate restTemplate;
  6. @GetMapping("/hello")
  7. @HystrixCommand(fallbackMethod = "fall")
  8. public String sayHello() {
  9. return restTemplate.getForObject("http://localhost:8080/server/hello", String.class);
  10. }
  11. /**
  12. * 服务端调用失败时,调用的回退方法
  13. *
  14. * 该方法要与@HystrixCommand标记的方法,具有相同的参数,和返回值类型
  15. *
  16. * @return
  17. */
  18. private String fall() {
  19. return "啊哦,服务调用出错了";
  20. }
  21. }

在ClientController中去完成对服务端接口的调用,并在调用的方法上使用@HystrixCommand标注。通过属性fallbackMethod来指定回退方法。

(2)在Spring Boot引导类上使用@EnableHystrix@EnableCircuitBreaker激活Hystrix功能。

对于编码方式使用Hystrix,不需要使用两个注解激活功能。Spring Cloud Netflix Hystrix使用Spring AOP功能对@HystrixCommand注解的方法进行拦截处理,@Enable*注解功能就是开启AOP功能。

  1. @SpringBootApplication
  2. // @EnableHystrix
  3. @EnableCircuitBreaker
  4. public class ClientApplication {
  5. public static void main(String[] args) {
  6. SpringApplication.run(ClientApplication.class, args);
  7. }
  8. }

█ 相关类

(1)@HystrixCommand

注解标注在方法上,用于对标注的方法完成Hystrix功能。

  1. @Target({ElementType.METHOD})
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Inherited
  4. @Documented
  5. public @interface HystrixCommand {
  6. String groupKey() default "";
  7. String commandKey() default "";
  8. String threadPoolKey() default "";
  9. String fallbackMethod() default "";
  10. HystrixProperty[] commandProperties() default {};
  11. HystrixProperty[] threadPoolProperties() default {};
  12. Class<? extends Throwable>[] ignoreExceptions() default {};
  13. ObservableExecutionMode observableExecutionMode() default ObservableExecutionMode.EAGER;
  14. HystrixException[] raiseHystrixExceptions() default {};
  15. String defaultFallback() default "";
  16. }
  • groupKey

指定当前command的分组名,用于组织command的报告、监控、仪表盘等。默认为@HystrixCommand标注的方法所在的类名。

  • commandKey

指定当前command的名称,默认为@HystrixCommand标注的方法名。

  • threadPoolKey

指定当前command使用的线程池名称,也是用于对其进行监控等功能时用于标记。

  • fallbackMethod

指定当方法调用发生熔断时的回退方法,回退方法要与@HystrixCommand标注的方法具有相同的参数签名和返回值类型。

  • commandProperties

指定相关配置,用来控制Hystrix的一些行为属性,比如超时时间,超过多长时间没有返回结果就发生熔断。

  1. @HystrixCommand(commandProperties = {
  2. @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1000"),
  3. @HystrixProperty(name = "execution.isolation.thread.interruptOnTimeout", value = "false")
  4. })
  • threadPoolProperties

指定线程池相关配置,用来控制线程池的一些行为。比如核心线程数等。

  1. @HystrixCommand(threadPoolProperties = {
  2. @HystrixProperty(name = "coreSize", value = "20"),
  3. @HystrixProperty(name = "keepAliveTimeMinutes", value = "1000")
  4. })
  • ignoreExceptions

指定忽略的异常,当方法执行过程中抛出忽略的异常,则不发生熔断,不调用回退方法,直接将异常返回给调用者。

  • observableExecutionMode

指定执行hystrix observable command的模式,默认为ObservableExecutionMode.EAGER饿汉模式,还有ObservableExecutionMode.LAZY懒汉模式。EAGER模式调用HystrixObservable#observe(),LAZY模式调用HystrixObservable#toObservable()

@HystrixCommand(ignoreExceptions = NullPointerException.class)
  • raiseHystrixExceptions

可配置HystrixException.RUNTIME_EXCEPTION,当配置RUNTIME_EXCEPTION时,所有方法抛出的没有被忽略的异常都被被封装成HystrixRuntimeException

@HystrixCommand(raiseHystrixExceptions= HystrixException.RUNTIME_EXCEPTION)
  • defaultFallback

指定默认的回退方法名,与fallbackMethod作用相同,当两者同时配置了,优先使用fallbackMethod配置。defaultFallback指定的方法不能有参数,并且返回值类型要与@HystrixCommand标注的方法相同。

(2)HystrixCommand

  1. public abstract class HystrixCommand<R> extends AbstractCommand<R>
  2. implements HystrixExecutable<R>, HystrixInvokableInfo<R>, HystrixObservable<R>

抽象类com.netflix.hystrix.HystrixCommand,通过其完成方法调用的熔断处理,是Hystrix功能的核心类。如何使用HystrixCommand完成熔断功能,在上面已经介绍过了。在主要来介绍HystrixCommand的执行。HystrixCommand提供了四种执行方式,对应两个方法:execute()、queue()、observe()、toObservable()

  1. {
  2. // 同步调用
  3. hystrixCommand.execute();
  4. // 异步调用
  5. Future<String> future = hystrixCommand.queue();
  6. future.get();
  7. // Observable饿汉式调用
  8. hystrixCommand.observe();
  9. // Observable懒汉式调用
  10. hystrixCommand.toObservable();
  11. }

HystrixObservableCommand提供了响应式编程API,其只具有observe()、toObservable()两种执行方式。

(3)@EnableHystrix

开启Hystrix功能,在@EnableHystrix中包含了@EnableCircuitBreaker,实际上是@EnableCircuitBreaker起的作用。

  1. @Target(ElementType.TYPE)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. @Inherited
  5. @EnableCircuitBreaker
  6. public @interface EnableHystrix {
  7. }

(4)@EnableCircuitBreaker

引入EnableCircuitBreakerImportSelector,功能转交给EnableCircuitBreakerImportSelector

  1. @Target(ElementType.TYPE)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. @Inherited
  5. @Import(EnableCircuitBreakerImportSelector.class)
  6. public @interface EnableCircuitBreaker {
  7. }

(5)EnableCircuitBreakerImportSelector

继承了SpringFactoryImportSelector。可通过配置spring.cloud.circuit.breaker.enabled控制HyStrix功能的开启,默认为true开启。

  1. @Order(Ordered.LOWEST_PRECEDENCE - 100)
  2. public class EnableCircuitBreakerImportSelector
  3. extends SpringFactoryImportSelector<EnableCircuitBreaker> {
  4. @Override
  5. protected boolean isEnabled() {
  6. return getEnvironment().getProperty("spring.cloud.circuit.breaker.enabled",
  7. Boolean.class, Boolean.TRUE);
  8. }
  9. }

SpringFactoryImportSelector的功能是使用SPI功能会去扫描类路径下的文件META-INF/spring.factories文件中,泛型类型开头的key,将对应的value值加载到Spring容器中。此处的泛型类型是EnableCircuitBreaker。在org.springframework.cloud:spring-cloud-netflix-hystrix的jar包下面META-INF/spring.factories中:

  1. org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker=\
  2. org.springframework.cloud.netflix.hystrix.HystrixCircuitBreakerConfiguration

(6)HystrixCircuitBreakerConfiguration

配置类,装配Bean。其中的HystrixCommandAspect就是结合注解@HystrixCommand完成对目标方法的拦截处理从而实现Hystrix的相关功能的。

  1. @Configuration(proxyBeanMethods = false)
  2. public class HystrixCircuitBreakerConfiguration {
  3. @Bean
  4. public HystrixCommandAspect hystrixCommandAspect() {
  5. return new HystrixCommandAspect();
  6. }
  7. @Bean
  8. public HystrixShutdownHook hystrixShutdownHook() {
  9. return new HystrixShutdownHook();
  10. }
  11. @Bean
  12. public HasFeatures hystrixFeature() {
  13. return HasFeatures
  14. .namedFeatures(new NamedFeature("Hystrix", HystrixCommandAspect.class));
  15. }
  16. /**
  17. * {@link DisposableBean} that makes sure that Hystrix internal state is cleared when
  18. * {@link ApplicationContext} shuts down.
  19. */
  20. private class HystrixShutdownHook implements DisposableBean {
  21. @Override
  22. public void destroy() throws Exception {
  23. // Just call Hystrix to reset thread pool etc.
  24. Hystrix.reset();
  25. }
  26. }
  27. }

(7)HystrixCommandAspect

Spring AOP功能中的切面。实现对注解@HystrixCommand标注的方法进行增强处理。

  1. @Aspect
  2. public class HystrixCommandAspect {
  3. private static final Map<HystrixPointcutType, MetaHolderFactory> META_HOLDER_FACTORY_MAP;
  4. static {
  5. META_HOLDER_FACTORY_MAP = ImmutableMap.<HystrixPointcutType, MetaHolderFactory>builder()
  6. .put(HystrixPointcutType.COMMAND, new CommandMetaHolderFactory())
  7. .put(HystrixPointcutType.COLLAPSER, new CollapserMetaHolderFactory())
  8. .build();
  9. }
  10. @Pointcut("@annotation(com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand)")
  11. public void hystrixCommandAnnotationPointcut() {
  12. }
  13. @Pointcut("@annotation(com.netflix.hystrix.contrib.javanica.annotation.HystrixCollapser)")
  14. public void hystrixCollapserAnnotationPointcut() {
  15. }
  16. @Around("hystrixCommandAnnotationPointcut() || hystrixCollapserAnnotationPointcut()")
  17. public Object methodsAnnotatedWithHystrixCommand(final ProceedingJoinPoint joinPoint) throws Throwable {
  18. Method method = getMethodFromTarget(joinPoint);
  19. Validate.notNull(method, "failed to get method from joinPoint: %s", joinPoint);
  20. if (method.isAnnotationPresent(HystrixCommand.class) && method.isAnnotationPresent(HystrixCollapser.class)) {
  21. throw new IllegalStateException("method cannot be annotated with HystrixCommand and HystrixCollapser " +
  22. "annotations at the same time");
  23. }
  24. MetaHolderFactory metaHolderFactory = META_HOLDER_FACTORY_MAP.get(HystrixPointcutType.of(method));
  25. MetaHolder metaHolder = metaHolderFactory.create(joinPoint);
  26. HystrixInvokable invokable = HystrixCommandFactory.getInstance().create(metaHolder);
  27. ExecutionType executionType = metaHolder.isCollapserAnnotationPresent() ?
  28. metaHolder.getCollapserExecutionType() : metaHolder.getExecutionType();
  29. Object result;
  30. try {
  31. if (!metaHolder.isObservable()) {
  32. result = CommandExecutor.execute(invokable, executionType, metaHolder);
  33. } else {
  34. result = executeObservable(invokable, executionType, metaHolder);
  35. }
  36. } catch (HystrixBadRequestException e) {
  37. throw e.getCause();
  38. } catch (HystrixRuntimeException e) {
  39. throw hystrixRuntimeExceptionToThrowable(metaHolder, e);
  40. }
  41. return result;
  42. }
  43. ......
  44. }

(8)HystrixCircuitBreaker

熔断器。如果方法执行失败的次数达到了设定的阈值(比如10秒内100次调用失败了20次以上),熔断器开启,就不再去执行目标方法,只能调用回退方法。然后,它将允许在一个定义的睡眠窗口(时间)之后进行一次重试,直到重试执行成功,如果执行成功,则熔断器关闭,能够继续执行目标方法。


█ HystrixProperty

戳《官方教程

@HystrixCommand注解,属性commandProperties和threadPoolProperties可配置的值。

  • commandProperties

(1)Execution相关:控制HystrixCommand.run()方法的行为

①execution.isolation.strategy

可选值THREADSEMAPHORE,默认为THREAD。THREAD:它在单独的线程上执行,并发请求受到线程池中线程数量的限制。SEMAPHORE:它在调用线程上执行,并发请求受到信号量计数的限制

②execution.isolation.thread.timeoutInMilliseconds

run方法运行超时时间,单位为毫秒,默认为1000。超过设置的时间会发生熔断。

③execution.timeout.enabled

超时功能是否开启,默认为true。

④execution.isolation.thread.interruptOnTimeout

当超时,运行方法的线程是否中断,默认为true。

⑤execution.isolation.thread.interruptOnCancel

作用同execution.isolation.thread.interruptOnTimeout。

⑥execution.isolation.semaphore.maxConcurrentRequests

当execution.isolation.strategy设置成SEMAPHORE,run方法并发访问的最大的请求数,默认为10。当请求数达到设置的阈值,后续的请求将会被拒绝。

(2)Fallback相关:控制HystrixCommand.getFallback()方法的行为

①fallback.isolation.semaphore.maxConcurrentRequests

请求回退方法的最大并发访问数,默认值为10。当达到设置的阈值,后续的请求会被拒绝,因为无法完成调用回退方法,因此会将run方法产生的异常抛出给调用者。

②fallback.enabled

调用回退方法是否可用,默认为true。

(3)Circuit Breaker相关:控制HystrixCircuitBreaker的行为

①circuitBreaker.enabled

熔断器功能是否可用,默认为true。

②circuitBreaker.requestVolumeThreshold

设置将触发熔断的滚动窗口中的最小请求数。

③circuitBreaker.sleepWindowInMilliseconds

触发熔断开启后,在设置时间范围内(默认为5000毫秒)的请求将会直接被熔断,不会调用run方法,直接调用回退方法。当达到设置的时间将会再次调用run方法确认是否能成功以此来判断是否应该关闭熔断功能。

④circuitBreaker.errorThresholdPercentage

设置错误请求的百分比,当错误的请求比达到此值,会发生熔断,调用回退方法。

⑤circuitBreaker.forceOpen

强制开启熔断,所有的请求都会被拒绝。

⑥circuitBreaker.forceClosed

强制关闭熔断。

(4)Metrics相关:控制获取与HystrixCommand和HystrixObservableCommand相关的指标。

①metrics.rollingStats.timeInMilliseconds

②metrics.rollingStats.numBuckets

③metrics.rollingPercentile.enabled

④metrics.rollingPercentile.timeInMilliseconds

⑤metrics.rollingPercentile.numBuckets

⑥metrics.rollingPercentile.bucketSize

⑦metrics.healthSnapshot.intervalInMilliseconds

(5)Request Context相关:控制请求上下文。

①requestCache.enabled

缓存是否可用。

②requestLog.enabled

请求日志是否可用。

  • threadPoolProperties

控制执行run方法的线程池的相关参数。

①coreSize

②maximumSize

③maxQueueSize

④queueSizeRejectionThreshold

⑤keepAliveTimeMinutes

⑥allowMaximumSizeToDivergeFromCoreSize

⑦metrics.rollingStats.timeInMilliseconds

⑧metrics.rollingStats.numBuckets

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/536939
推荐阅读
相关标签
  

闽ICP备14008679号