赞
踩
关键词:服务短路 服务熔断 服务降级 Hystrix
最近工作上面比较忙,学习稍微耽搁了。现在让我们继续学习Spring Cloud系列之Hystrix。
Hystrix作为服务熔断与降级的工具,既可以用在服务提供者方,也可以用在服务调用者方。本文例子是在服务调用方使用Hystrix。
1、搭建服务端,用于提供服务:
创建Controller,调用接口为:/server/hello。以8080作为服务端口启动服务。
- @RestController
- @RequestMapping("/server")
- public class Server8080Controller {
-
- private Random random = new Random(10);
-
- @GetMapping("/hello")
- public String sayHello() throws Exception{
- // 当产生的随机数大于5,就抛出异常,否则正常返回值
- if (random.nextInt() > 5) {
- throw new IllegalAccessException("服务端发生异常");
- }
-
- return "server hello world";
- }
-
- }
2、搭建客户端,用于调用服务端接口:
(1)引入Spring Cloud Netflix Hystrix依赖
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
- </dependency>
(2)创建Controller
- @RestController
- @RequestMapping("/client")
- public class ClientController {
-
- private RestTemplate restTemplate = new RestTemplate();
-
- @GetMapping("/hello")
- public String sayHello() throws Exception{
- return null;
- }
- }
如何使用Hystrix功能,有两种方式,编码式和声明式。
1、编码式
(1)通过继承HystrixCommand(注意是抽象类com.netflix.hystrix.HystrixCommand),实现run()方法,重写getFallback()方法:
- public class CustomHystrixCommand extends HystrixCommand<String> {
-
- private final RestTemplate restTemplate;
-
- // 因为HystrixCommand中没有提供无参构造器,所以子类要显示调用super有参构造器
- // 默认的无参构造器会默认调用父类的super无参构造器
- public CustomHystrixCommand(String groupName, RestTemplate restTemplate) {
- super(HystrixCommandGroupKey.Factory.asKey(groupName));
- this.restTemplate = restTemplate;
- }
-
- @Override
- protected String run() throws Exception {
- return restTemplate.getForObject("http://localhost:8080/server/hello", String.class);
- }
-
- @Override
- protected String getFallback() {
- return "CustomHystrixCommandImpl getFallback";
- }
- }
在run方法中,通过RestTemplate调用服务端接口,当run方法正确执行,可获取到服务端返回值。当发生异常时,会进行熔断,调用getFallback方法,将getFallback方法的返回值返回给调用者。通过HystrixCommand<String>中使用泛型,来标注方法返回值类型,不使用泛型,则为Object类型。
(2)修改客户端ClientController,使用CustomHystrixCommand:
- @RestController
- @RequestMapping("/client")
- public class ClientController {
-
- private RestTemplate restTemplate = new RestTemplate();
-
- @GetMapping("/hello")
- public String sayHello() throws Exception{
- CustomHystrixCommand customHystrixCommand = new CustomHystrixCommand("customHystrixCommand", restTemplate);
- return customHystrixCommand.execute();
- }
-
-
- }
通过调用HystrixCommand的execute()方法,最终会调用到run()方法。这种方式比较像线程Thread的API。
(3)Hystrix也提供了响应式编程模型,此时可结合使用Spring WebFlux进行服务端接口调用。通过继承HystrixObservableCommand实现响应式编程:
- public class CustomHystrixObservableCommand extends HystrixObservableCommand {
-
- public CustomHystrixObservableCommand(String groupName) {
- super(HystrixCommandGroupKey.Factory.asKey(groupName));
- }
-
- @Override
- protected Observable construct() {
- return Observable.just("在这里可以使用Spring WebFlux进行服务端调用");
- }
-
- @Override
- protected Observable resumeWithFallback() {
- return Observable.just("回退方法");
- }
- }
2、声明式
声明式方式是在方法上标注注解@HystrixCommand,通过属性fallbackMethod配置熔断后调用的回退方法。
(1)修改客户端ClientController:
- @RestController
- @RequestMapping("/rest/client")
- public class ClientController {
-
- @Autowired
- private RestTemplate restTemplate;
-
- @GetMapping("/hello")
- @HystrixCommand(fallbackMethod = "fall")
- public String sayHello() {
- return restTemplate.getForObject("http://localhost:8080/server/hello", String.class);
- }
-
- /**
- * 服务端调用失败时,调用的回退方法
- *
- * 该方法要与@HystrixCommand标记的方法,具有相同的参数,和返回值类型
- *
- * @return
- */
- private String fall() {
- return "啊哦,服务调用出错了";
- }
-
- }
在ClientController中去完成对服务端接口的调用,并在调用的方法上使用@HystrixCommand标注。通过属性fallbackMethod来指定回退方法。
(2)在Spring Boot引导类上使用@EnableHystrix或@EnableCircuitBreaker激活Hystrix功能。
对于编码方式使用Hystrix,不需要使用两个注解激活功能。Spring Cloud Netflix Hystrix使用Spring AOP功能对@HystrixCommand注解的方法进行拦截处理,@Enable*注解功能就是开启AOP功能。
- @SpringBootApplication
- // @EnableHystrix
- @EnableCircuitBreaker
- public class ClientApplication {
-
- public static void main(String[] args) {
- SpringApplication.run(ClientApplication.class, args);
- }
-
- }
(1)@HystrixCommand
注解标注在方法上,用于对标注的方法完成Hystrix功能。
- @Target({ElementType.METHOD})
- @Retention(RetentionPolicy.RUNTIME)
- @Inherited
- @Documented
- public @interface HystrixCommand {
-
- String groupKey() default "";
-
- String commandKey() default "";
-
- String threadPoolKey() default "";
-
- String fallbackMethod() default "";
-
- HystrixProperty[] commandProperties() default {};
-
- HystrixProperty[] threadPoolProperties() default {};
-
- Class<? extends Throwable>[] ignoreExceptions() default {};
-
- ObservableExecutionMode observableExecutionMode() default ObservableExecutionMode.EAGER;
-
- HystrixException[] raiseHystrixExceptions() default {};
-
- String defaultFallback() default "";
- }
指定当前command的分组名,用于组织command的报告、监控、仪表盘等。默认为@HystrixCommand标注的方法所在的类名。
指定当前command的名称,默认为@HystrixCommand标注的方法名。
指定当前command使用的线程池名称,也是用于对其进行监控等功能时用于标记。
指定当方法调用发生熔断时的回退方法,回退方法要与@HystrixCommand标注的方法具有相同的参数签名和返回值类型。
指定相关配置,用来控制Hystrix的一些行为属性,比如超时时间,超过多长时间没有返回结果就发生熔断。
- @HystrixCommand(commandProperties = {
- @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1000"),
- @HystrixProperty(name = "execution.isolation.thread.interruptOnTimeout", value = "false")
- })
指定线程池相关配置,用来控制线程池的一些行为。比如核心线程数等。
- @HystrixCommand(threadPoolProperties = {
- @HystrixProperty(name = "coreSize", value = "20"),
- @HystrixProperty(name = "keepAliveTimeMinutes", value = "1000")
- })
指定忽略的异常,当方法执行过程中抛出忽略的异常,则不发生熔断,不调用回退方法,直接将异常返回给调用者。
指定执行hystrix observable command的模式,默认为ObservableExecutionMode.EAGER饿汉模式,还有ObservableExecutionMode.LAZY懒汉模式。EAGER模式调用HystrixObservable#observe(),LAZY模式调用HystrixObservable#toObservable()。
@HystrixCommand(ignoreExceptions = NullPointerException.class)
可配置HystrixException.RUNTIME_EXCEPTION,当配置RUNTIME_EXCEPTION时,所有方法抛出的没有被忽略的异常都被被封装成HystrixRuntimeException。
@HystrixCommand(raiseHystrixExceptions= HystrixException.RUNTIME_EXCEPTION)
指定默认的回退方法名,与fallbackMethod作用相同,当两者同时配置了,优先使用fallbackMethod配置。defaultFallback指定的方法不能有参数,并且返回值类型要与@HystrixCommand标注的方法相同。
(2)HystrixCommand
- public abstract class HystrixCommand<R> extends AbstractCommand<R>
- implements HystrixExecutable<R>, HystrixInvokableInfo<R>, HystrixObservable<R>
抽象类com.netflix.hystrix.HystrixCommand,通过其完成方法调用的熔断处理,是Hystrix功能的核心类。如何使用HystrixCommand完成熔断功能,在上面已经介绍过了。在主要来介绍HystrixCommand的执行。HystrixCommand提供了四种执行方式,对应两个方法:execute()、queue()、observe()、toObservable()。
- {
- // 同步调用
- hystrixCommand.execute();
-
- // 异步调用
- Future<String> future = hystrixCommand.queue();
- future.get();
-
- // Observable饿汉式调用
- hystrixCommand.observe();
-
- // Observable懒汉式调用
- hystrixCommand.toObservable();
- }
HystrixObservableCommand提供了响应式编程API,其只具有observe()、toObservable()两种执行方式。
(3)@EnableHystrix
开启Hystrix功能,在@EnableHystrix中包含了@EnableCircuitBreaker,实际上是@EnableCircuitBreaker起的作用。
- @Target(ElementType.TYPE)
- @Retention(RetentionPolicy.RUNTIME)
- @Documented
- @Inherited
- @EnableCircuitBreaker
- public @interface EnableHystrix {
-
- }
(4)@EnableCircuitBreaker
引入EnableCircuitBreakerImportSelector,功能转交给EnableCircuitBreakerImportSelector
- @Target(ElementType.TYPE)
- @Retention(RetentionPolicy.RUNTIME)
- @Documented
- @Inherited
- @Import(EnableCircuitBreakerImportSelector.class)
- public @interface EnableCircuitBreaker {
-
- }
(5)EnableCircuitBreakerImportSelector
继承了SpringFactoryImportSelector。可通过配置spring.cloud.circuit.breaker.enabled控制HyStrix功能的开启,默认为true开启。
- @Order(Ordered.LOWEST_PRECEDENCE - 100)
- public class EnableCircuitBreakerImportSelector
- extends SpringFactoryImportSelector<EnableCircuitBreaker> {
-
- @Override
- protected boolean isEnabled() {
- return getEnvironment().getProperty("spring.cloud.circuit.breaker.enabled",
- Boolean.class, Boolean.TRUE);
- }
-
- }
SpringFactoryImportSelector的功能是使用SPI功能会去扫描类路径下的文件META-INF/spring.factories文件中,泛型类型开头的key,将对应的value值加载到Spring容器中。此处的泛型类型是EnableCircuitBreaker。在org.springframework.cloud:spring-cloud-netflix-hystrix的jar包下面META-INF/spring.factories中:
- org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker=\
- org.springframework.cloud.netflix.hystrix.HystrixCircuitBreakerConfiguration
(6)HystrixCircuitBreakerConfiguration
配置类,装配Bean。其中的HystrixCommandAspect就是结合注解@HystrixCommand完成对目标方法的拦截处理从而实现Hystrix的相关功能的。
- @Configuration(proxyBeanMethods = false)
- public class HystrixCircuitBreakerConfiguration {
-
- @Bean
- public HystrixCommandAspect hystrixCommandAspect() {
- return new HystrixCommandAspect();
- }
-
- @Bean
- public HystrixShutdownHook hystrixShutdownHook() {
- return new HystrixShutdownHook();
- }
-
- @Bean
- public HasFeatures hystrixFeature() {
- return HasFeatures
- .namedFeatures(new NamedFeature("Hystrix", HystrixCommandAspect.class));
- }
-
- /**
- * {@link DisposableBean} that makes sure that Hystrix internal state is cleared when
- * {@link ApplicationContext} shuts down.
- */
- private class HystrixShutdownHook implements DisposableBean {
-
- @Override
- public void destroy() throws Exception {
- // Just call Hystrix to reset thread pool etc.
- Hystrix.reset();
- }
-
- }
-
- }
(7)HystrixCommandAspect
Spring AOP功能中的切面。实现对注解@HystrixCommand标注的方法进行增强处理。
- @Aspect
- public class HystrixCommandAspect {
-
- private static final Map<HystrixPointcutType, MetaHolderFactory> META_HOLDER_FACTORY_MAP;
-
- static {
- META_HOLDER_FACTORY_MAP = ImmutableMap.<HystrixPointcutType, MetaHolderFactory>builder()
- .put(HystrixPointcutType.COMMAND, new CommandMetaHolderFactory())
- .put(HystrixPointcutType.COLLAPSER, new CollapserMetaHolderFactory())
- .build();
- }
-
- @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 = getMethodFromTarget(joinPoint);
- Validate.notNull(method, "failed to get method from joinPoint: %s", joinPoint);
- 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");
- }
- MetaHolderFactory metaHolderFactory = META_HOLDER_FACTORY_MAP.get(HystrixPointcutType.of(method));
- MetaHolder metaHolder = metaHolderFactory.create(joinPoint);
- HystrixInvokable invokable = HystrixCommandFactory.getInstance().create(metaHolder);
- ExecutionType executionType = metaHolder.isCollapserAnnotationPresent() ?
- metaHolder.getCollapserExecutionType() : metaHolder.getExecutionType();
-
- Object result;
- try {
- if (!metaHolder.isObservable()) {
- result = CommandExecutor.execute(invokable, executionType, metaHolder);
- } else {
- result = executeObservable(invokable, executionType, metaHolder);
- }
- } catch (HystrixBadRequestException e) {
- throw e.getCause();
- } catch (HystrixRuntimeException e) {
- throw hystrixRuntimeExceptionToThrowable(metaHolder, e);
- }
- return result;
- }
-
- ......
- }
(8)HystrixCircuitBreaker
熔断器。如果方法执行失败的次数达到了设定的阈值(比如10秒内100次调用失败了20次以上),熔断器开启,就不再去执行目标方法,只能调用回退方法。然后,它将允许在一个定义的睡眠窗口(时间)之后进行一次重试,直到重试执行成功,如果执行成功,则熔断器关闭,能够继续执行目标方法。
戳《官方教程》
@HystrixCommand注解,属性commandProperties和threadPoolProperties可配置的值。
(1)Execution相关:控制HystrixCommand.run()方法的行为
①execution.isolation.strategy
可选值THREAD、SEMAPHORE,默认为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
请求日志是否可用。
控制执行run方法的线程池的相关参数。
①coreSize
②maximumSize
③maxQueueSize
④queueSizeRejectionThreshold
⑤keepAliveTimeMinutes
⑥allowMaximumSizeToDivergeFromCoreSize
⑦metrics.rollingStats.timeInMilliseconds
⑧metrics.rollingStats.numBuckets
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。