Spring Cloud 服务容错 Hystrix 入门

1 Hystrix 概述
在开始 Spring Cloud Hystrix 的学习之前,我们先来一起看看服务雪崩的概念,方便我们理解为什么要使用 Hystrix 框架。
1.1 服务雪崩
在复杂的分布式架构中,服务之间都是相互依赖的,通过 HTTP 或者 RPC 进行远程调用。正常情况下,用户请求依赖的各个服务节点都能快速响应,如下图所示:

在这里插入图片描述
当一个依赖的节点坏掉时,将阻塞用户整个请求,并且占用的系统线程资源无法释放:
在这里插入图片描述
如果上述情况持续一段时间,服务提供者的响应一直很慢或无法响应,会导致服务消费者的响应也跟着很慢,最终引起服务消费者的请求任务积压,也跟着一起出问题了。这种因为一个下游服务的故障,导致上游服务一起跟着故障的现象,我们称为“服务雪崩”,而Spring Cloud Hystrix通过资源隔离和熔断功能就可以帮助我们解决服务雪崩的问题。
在这里插入图片描述

1.2 Hystrix简介
Hystrix是Netflix开源的一款分布式系统的延迟和容错库,目的是用来隔离分布式服务故障。在分布式环境中,不可避免地会遇到所依赖的服务挂掉或者响应特别慢的情况,Hystrix 在服务与服务之间建立了一个中间层,防止服务之间出现故障,并提供了失败时的 fallback 策略,来增加系统的整体可靠性和弹性。

Hystrix 简介官方文档
Hystrix 配置属性官方文档

1.3 Hystrix的特点
  • 防止级联故障,并提供优雅降级功能。
  • 通过线程隔离和信号量隔离,限制使用分布式服务的资源,当某一个服务出现问题不会影响其他服务调用。
  • 提供优雅降级机制:超时降级、资源不足时(线程或者信号量)降级,降级后可以配合降级接口返回托底数据。
  • 提供熔断器实现,当失败率达到阈值自动触发降级(如因网络故障或超时造成的失败率高),当接口可以正常访问时熔断器触发的快速失败会进行快速恢复。
  • 提供了请求缓存和请求合并的功能。
2 Hystrix 快速使用
pom文件添加hystrix依赖
<parent>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-parent</artifactId>
   <version>2.3.0.RELEASE</version>
   <relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>hystrix-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>hystrix-demo</name>
<description>Demo project for Spring Boot</description>

<properties>
   <java.version>1.8</java.version>
   <spring-cloud.version>Hoxton.SR9</spring-cloud.version>
</properties>

<dependencyManagement>
   <dependencies>
       <dependency>
           <groupId>org.springframework.cloud</groupId>
           <artifactId>spring-cloud-dependencies</artifactId>
           <version>${spring-cloud.version}</version>
           <type>pom</type>
           <scope>import</scope>
       </dependency>
   </dependencies>
</dependencyManagement>

<dependencies>
   <!--hystrix依赖-->
   <dependency>
       <groupId>org.springframework.cloud</groupId>
       <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
   </dependency>
   
   <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-web</artifactId>
   </dependency>

   <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-test</artifactId>
       <scope>test</scope>
   </dependency>

   <dependency>
       <groupId>org.springframework.cloud</groupId>
       <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
   </dependency>
</dependencies>
bootstrap.yml文件增加eureka配置
spring:
  application:
    name: hystrix-demo
  profiles:
    active: dev
server:
  port: 3020
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8090/eureka/
  instance:
    lease-expiration-duration-in-seconds: 30
    lease-renewal-interval-in-seconds: 10
    prefer-ip-address: true
    instance-id: ${spring.cloud.client.ip-address}:${server.port}
Application启动类增加@EnableHystrix注解表示开启Hystrix功能
@EnableHystrix
@SpringBootApplication
public class HystrixDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(HystrixDemoApplication.class, args);
    }

    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}
成功启动项目,表示初始搭建完成。
3 Spring Cloud Hystrix 资源隔离
Hystrix 通过线程池和信号量(Semaphore) 两种模式来实现资源隔离。

在这里插入图片描述

3.1 线程池隔离
默认情况下,Hystrix 采用线程池模式来实现资源隔离。针对调用的每一个服务,我们给其单独分配一个线程池。

新增HystrixDemoController控制器
@RestController
public class HystrixDemoController {
    @Autowired
    private HystrixDemoService hystrixDemoService;
    /**
     * 线程池隔离
     */
    @GetMapping("/hystrixCalculateAddThread")
    public BigDecimal hystrixCalculateAddThread(BigDecimal number1, BigDecimal number2) {
        BigDecimal result = hystrixDemoService.hystrixCalculateAddThread(number1, number2);
        return result;
    }
}
新增HystrixDemoService服务类
@Component
public class HystrixDemoService {
    private Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    private RestTemplate restTemplate;

    /**
     * 资源隔离之线程池隔离
     */
    @HystrixCommand(
            groupKey = "ThreadPoolGroupKey",
            commandKey = "ThreadPoolCommandKey",
            threadPoolKey = "ThreadPoolKey",
            fallbackMethod = "hystrixCalculateAddThreadFallBack",
            commandProperties = {
                    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "5000"),
                    @HystrixProperty(name = "execution.timeout.enabled", value = "true"),
                    @HystrixProperty(name = "execution.isolation.strategy", value = "THREAD")
            },
            threadPoolProperties = {
                    @HystrixProperty(name = "coreSize", value = "15")
            }
    )
    public BigDecimal hystrixCalculateAddThread(BigDecimal number1, BigDecimal number2) {
        logger.info("资源隔离之线程池隔离开始......");
        String url = String.format("http://127.0.0.1:3030/providerCalculateAdd?number1=%s&number2=%s", number1, number2);
        BigDecimal result = restTemplate.getForObject(url, BigDecimal.class);
        return result;
    }

    public BigDecimal hystrixCalculateAddThreadFallBack(BigDecimal number1, BigDecimal number2, Throwable throwable) {
        logger.info("[hystrixCalculateAddThreadFallBack][number1({}),number2({}): exception({})]", number1, number2, ExceptionUtils.getRootCauseMessage(throwable));
        return BigDecimal.ZERO;
    }
}
  • hystrixCalculateAddThread方法通过RestTemplate调用provider-demo服务计算两数之和的方法。
  • @HystrixCommand注解的属性fallbackMethod用来设置fallback方法,注意fallbackMethod方法的参数要和原始方法保持一致,最后再增加一个Throwable异常类方便获取异常信息。
  • @HystrixCommand注解参数使用说明
参数名称说明
groupKeyHystrix Command 命令分组键,默认未配置情况下使用方法所在类名
commandKeyHystrix Command 命令键,默认未配置情况下使用方法名
threadPoolKey线程池名,用于划分不同的线程池,进行资源隔离。默认未配置情况下,相同 groupKey 的 Hystrix Command 使用同一个线程池;在配置情况下,相同 groupKey + threadPoolKey 使用同一个线程池。
fallbackMethod服务降级的处理方法
thread.timeoutInMilliseconds请求超时时长,单位毫秒
execution.timeout.enabled是否开启超时设置,默认为true
execution.isolation.strategy资源隔离实现策略,线程池隔离:THREAD,信号量隔离:SEMAPHORE
coreSize核心线程数
provider-demo服务的providerCalculateAdd方法
@GetMapping("providerCalculateAdd")
public BigDecimal providerCalculateAdd(BigDecimal number1, BigDecimal number2) {
    logger.info("-----------providerCalculateAdd--------");
    try {
        Thread.sleep(2000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    BigDecimal ret = number1.add(number2);
    return ret;
}
启动各个服务,使用postman访问可以正常返回结果。

在这里插入图片描述
在这里插入图片描述
<1> 修改参数execution.isolation.thread.timeoutInMilliseconds=1000,重新启动hystrix-demo服务,由于providerCalculateAdd线程阻塞2000毫秒,所以会调用fallback方法。

图一,postman重新调用接口
在这里插入图片描述
图二,超时调用fallback方法,打印HystrixTimeoutException类
在这里插入图片描述

3.2 信号量隔离
使用线程池模式进行资源隔离时,需要进行上下文的切换,会带来一定的性能损耗。因此,如果对性能有较高要求,可以考虑采用信号量模式。信号量对服务起到限流保护的作用,例如服务A的信号量大小为10,那么同时只允许10个Tomcat线程来访问服务A,其他的请求将被拒绝。

HystrixDemoController增加信号量隔离的代码
/**
 * 信号量隔离
 */
@GetMapping("/hystrixCalculateAddSemaphore")
public BigDecimal hystrixCalculateAddSemaphore(BigDecimal number1, BigDecimal number2) {
    BigDecimal result = hystrixDemoService.hystrixCalculateAddSemaphore(number1, number2);
    return result;
}
hystrixDemoService增加信号量隔离的代码
/**
  * 资源隔离之信号量隔离
  */
 @HystrixCommand(
         groupKey = "SemaphoreGroupKey",
         commandKey = "SemaphoreCommandKey",
         threadPoolKey = "SemaphoreThreadPoolKey",
         fallbackMethod = "hystrixCalculateAddSemaphoreFallBack",
         commandProperties = {
                 @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "5000"),
                 @HystrixProperty(name = "execution.timeout.enabled", value = "true"),
                 @HystrixProperty(name = "execution.isolation.strategy", value = "SEMAPHORE"),
                 @HystrixProperty(name = "execution.isolation.semaphore.maxConcurrentRequests", value = "3"),
                 @HystrixProperty(name = "fallback.isolation.semaphore.maxConcurrentRequests", value = "1")
         }
 )
 public BigDecimal hystrixCalculateAddSemaphore(BigDecimal number1, BigDecimal number2) {
     logger.info("资源隔离之信号量隔离开始......");
     String url = String.format("http://127.0.0.1:3030/providerCalculateAdd?number1=%s&number2=%s", number1, number2);
     BigDecimal result = restTemplate.getForObject(url, BigDecimal.class);
     return result;
 }

 public BigDecimal hystrixCalculateAddSemaphoreFallBack(BigDecimal number1, BigDecimal number2, Throwable throwable) {
     logger.info("[hystrixCalculateAddSemaphore][number1({}),number2({}): exception({})]", number1, number2, ExceptionUtils.getRootCauseMessage(throwable));
     return BigDecimal.ZERO;
 }
信号量配置属性的作用说明:
  • execution.isolation.strategy=SEMAPHORE,表示资源隔离策略为信号量隔离
  • execution.isolation.semaphore.maxConcurrentRequests,表示信号量的大小,当最大并发请求数量达到该值时,后续请求将被拒绝。
  • fallback.isolation.semaphore.maxConcurrentRequests,表示设置并发调用fallback方法线程数
重新启动hystrix-demo服务,使用postman调用接口,可以正常返回。

在这里插入图片描述

使用Jmeter同时启动10个线程调用接口hystrixCalculateAddSemaphore,3个成功返回结果,其余7个走的fallback方法并打印无法获取信号量的异常信息。

2021-01-12 16:48:32.236  INFO 21520 --- [nio-3020-exec-2] c.e.h.service.HystrixDemoService         : 资源隔离之信号量隔离开始......
2021-01-12 16:48:32.236  INFO 21520 --- [nio-3020-exec-4] c.e.h.service.HystrixDemoService         : 资源隔离之信号量隔离开始......
2021-01-12 16:48:32.306  INFO 21520 --- [nio-3020-exec-5] c.e.h.service.HystrixDemoService         : 资源隔离之信号量隔离开始......
2021-01-12 16:48:32.421  INFO 21520 --- [nio-3020-exec-6] c.e.h.service.HystrixDemoService         : [hystrixCalculateAddSemaphore][number1(10),number2(20): exception(RuntimeException: could not acquire a semaphore for execution)]
2021-01-12 16:48:32.516  INFO 21520 --- [nio-3020-exec-8] c.e.h.service.HystrixDemoService         : [hystrixCalculateAddSemaphore][number1(10),number2(20): exception(RuntimeException: could not acquire a semaphore for execution)]
2021-01-12 16:48:32.621  INFO 21520 --- [io-3020-exec-10] c.e.h.service.HystrixDemoService         : [hystrixCalculateAddSemaphore][number1(10),number2(20): exception(RuntimeException: could not acquire a semaphore for execution)]
2021-01-12 16:48:32.721  INFO 21520 --- [nio-3020-exec-3] c.e.h.service.HystrixDemoService         : [hystrixCalculateAddSemaphore][number1(10),number2(20): exception(RuntimeException: could not acquire a semaphore for execution)]
2021-01-12 16:48:32.816  INFO 21520 --- [nio-3020-exec-7] c.e.h.service.HystrixDemoService         : [hystrixCalculateAddSemaphore][number1(10),number2(20): exception(RuntimeException: could not acquire a semaphore for execution)]
2021-01-12 16:48:32.916  INFO 21520 --- [nio-3020-exec-9] c.e.h.service.HystrixDemoService         : [hystrixCalculateAddSemaphore][number1(10),number2(20): exception(RuntimeException: could not acquire a semaphore for execution)]
2021-01-12 16:48:33.006  INFO 21520 --- [nio-3020-exec-1] c.e.h.service.HystrixDemoService         : [hystrixCalculateAddSemaphore][number1(10),number2(20): exception(RuntimeException: could not acquire a semaphore for execution)]
4 Spring Cloud Hystrix 熔断机制
熔断也叫过载保护,一般指软件系统中,由于某些原因,服务出现了过载现象,为防止造成整个系统故障,从而采用一种保护措施。如果某个服务调用慢或者有大量超时,Hystrix会熔断该服务的调用,对于后续调用请求不再继续调用目标服务,直接返回,快速释放资源。如果目标服务情况好转则恢复调用。需要同时满足时间、请求数、失败比例三个条件才会触发熔断功能。

HystrixDemoController添加熔断机制的代码
/**
 * 熔断机制
 */
@GetMapping("/hystrixCalculateAddCircuitBreaker")
public BigDecimal hystrixCalculateAddCircuitBreaker(BigDecimal number1, BigDecimal number2) {
    BigDecimal result = hystrixDemoService.hystrixCalculateAddCircuitBreaker(number1, number2);
    return result;
}
hystrixDemoService增加熔断机制的代码
@HystrixCommand(
            groupKey = "CircuitBreakerGroupKey",
            commandKey = "CircuitBreakerCommandKey",
            threadPoolKey = "CircuitBreakerThreadPoolKey",
            fallbackMethod = "hystrixCalculateAddCircuitBreakerFallBack",
            threadPoolProperties = {
                    @HystrixProperty(name = "coreSize", value = "20")
            },
            commandProperties = {
                    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "5000"),
                    @HystrixProperty(name = "execution.timeout.enabled", value = "true"),
                    @HystrixProperty(name = "circuitBreaker.enabled", value = "true"),
                    @HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds", value = "1000"),
                    @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),
                    @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50"),
                    @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "1000")
            }
    )
    public BigDecimal hystrixCalculateAddCircuitBreaker(BigDecimal number1, BigDecimal number2) {
        logger.info("测试熔断开始......");
        String url = String.format("http://127.0.0.1:3030/providerCalculateAdd?number1=%s&number2=%s", number1, number2);
        BigDecimal result = restTemplate.getForObject(url, BigDecimal.class);
        return result;
    }

    public BigDecimal hystrixCalculateAddCircuitBreakerFallBack(BigDecimal number1, BigDecimal number2, Throwable throwable) {
        logger.info("[hystrixCalculateAddCircuitBreakerFallBack][number1({}),number2({}): exception({})]", number1, number2, ExceptionUtils.getRootCauseMessage(throwable));
        return BigDecimal.ZERO;
    }
熔断相关的属性作用说明:
  • circuitBreaker.enabled=true,表示开启熔断机制
  • metrics.rollingStats.timeInMilliseconds,表示配置滚动时间窗长度,单位为毫秒。
  • circuitBreaker.requestVolumeThreshold,表示在滚动时间窗中,断路器熔断的最小请求数。
  • circuitBreaker.errorThresholdPercentage,表示可以打开断路器的接口请求失败的百分比条件
  • circuitBreaker.sleepWindowInMilliseconds,表示断路器的休眠时间窗长度,在指定时间之后,可以自动打开或者关闭熔断机制的功能。

由上述属性的说明可以得出例子中熔断器的触发条件:1秒内发送10个请求,如果有5个请求失败会立即打开熔断功能。

重新启动hystrix-demo服务,然后关闭provider-demo服务,使用Jmeter同时发起100个线程请求,保证接口执行大于1秒钟,通过控制台日志会发现熔断器已打开,请求线程会直接执行fallback方法。为了方便查看,再改成同时发起10个线程请求。

2021-01-12 17:32:55.568  INFO 14852 --- [ThreadPoolKey-1] c.e.h.service.HystrixDemoService         : 测试熔断开始......
2021-01-12 17:32:55.678  INFO 14852 --- [nio-3020-exec-8] c.e.h.service.HystrixDemoService         : [hystrixCalculateAddCircuitBreakerFallBack][number1(10),number2(20): exception(RuntimeException: Hystrix circuit short-circuited and is OPEN)]
2021-01-12 17:32:55.779  INFO 14852 --- [io-3020-exec-16] c.e.h.service.HystrixDemoService         : [hystrixCalculateAddCircuitBreakerFallBack][number1(10),number2(20): exception(RuntimeException: Hystrix circuit short-circuited and is OPEN)]
2021-01-12 17:32:55.878  INFO 14852 --- [io-3020-exec-19] c.e.h.service.HystrixDemoService         : [hystrixCalculateAddCircuitBreakerFallBack][number1(10),number2(20): exception(RuntimeException: Hystrix circuit short-circuited and is OPEN)]
2021-01-12 17:32:55.978  INFO 14852 --- [io-3020-exec-22] c.e.h.service.HystrixDemoService         : [hystrixCalculateAddCircuitBreakerFallBack][number1(10),number2(20): exception(RuntimeException: Hystrix circuit short-circuited and is OPEN)]
2021-01-12 17:32:56.078  INFO 14852 --- [io-3020-exec-12] c.e.h.service.HystrixDemoService         : [hystrixCalculateAddCircuitBreakerFallBack][number1(10),number2(20): exception(RuntimeException: Hystrix circuit short-circuited and is OPEN)]
2021-01-12 17:32:56.179  INFO 14852 --- [nio-3020-exec-4] c.e.h.service.HystrixDemoService         : [hystrixCalculateAddCircuitBreakerFallBack][number1(10),number2(20): exception(RuntimeException: Hystrix circuit short-circuited and is OPEN)]
2021-01-12 17:32:56.279  INFO 14852 --- [io-3020-exec-17] c.e.h.service.HystrixDemoService         : [hystrixCalculateAddCircuitBreakerFallBack][number1(10),number2(20): exception(RuntimeException: Hystrix circuit short-circuited and is OPEN)]
2021-01-12 17:32:56.378  INFO 14852 --- [nio-3020-exec-1] c.e.h.service.HystrixDemoService         : [hystrixCalculateAddCircuitBreakerFallBack][number1(10),number2(20): exception(RuntimeException: Hystrix circuit short-circuited and is OPEN)]
2021-01-12 17:32:56.488  INFO 14852 --- [nio-3020-exec-5] c.e.h.service.HystrixDemoService         : [hystrixCalculateAddCircuitBreakerFallBack][number1(10),number2(20): exception(RuntimeException: Hystrix circuit short-circuited and is OPEN)]
2021-01-12 17:32:57.639  INFO 14852 --- [ThreadPoolKey-1] c.e.h.service.HystrixDemoService         : [hystrixCalculateAddCircuitBreakerFallBack][number1(10),number2(20): exception(ConnectException: Connection refused: connect)]

5 Spring Cloud Hystrix请求缓存
Hystrix可将请求进行缓存,当后续有相同的请求时,直接返回缓存中的响应结果。从而减少直接对服务进行调用,减轻服务的压力。
5.1 使用请求缓存
Hystrix 支持在同一个 HystrixRequestContext 上下文中,提供缓存的功能。所以,创建 HystrixRequestContextFilter 过滤器类,初始化HystrixRequestContext 上下文。
@Component
@WebFilter(urlPatterns = "/")
public class HystrixRequestContextFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest,
                         ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        // 初始化 HystrixRequestContext
        HystrixRequestContext contextFilter = HystrixRequestContext.initializeContext();
        try {
            filterChain.doFilter(servletRequest, servletResponse);
        } finally {
            // 销毁 HystrixRequestContext
            contextFilter.close();
        }
    }
}
HystrixDemoController增加使用请求缓存的代码
/**
  * 使用请求缓存
  */
 @GetMapping("/hystrixCalculateAddCacheResult")
 public BigDecimal hystrixCalculateAddCacheResult(BigDecimal number1, BigDecimal number2) {
     BigDecimal resultA = hystrixDemoService.hystrixCalculateAddCacheResult(number1, number2);
     BigDecimal resultB = hystrixDemoService.hystrixCalculateAddCacheResult(number1, number2);
     BigDecimal resultC = hystrixDemoService.hystrixCalculateAddCacheResult(number1, number2);
     return resultC;
 }
hystrixDemoService增加请求缓存的代码,@CacheResult 注解添加在方法上,会将方法的执行结果进行缓存,cacheKeyMethod 属性用来设置缓存键的生成方法。
@CacheResult(cacheKeyMethod = "getCacheKey")
@HystrixCommand(
        groupKey = "CacheGroupKey",
        commandKey = "CacheCommandKey",
        threadPoolKey = "CacheThreadPoolKey",
        commandProperties = {
                @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "5000"),
                @HystrixProperty(name = "execution.timeout.enabled", value = "true"),
                @HystrixProperty(name = "execution.isolation.strategy", value = "THREAD")
        },
        threadPoolProperties = {
                @HystrixProperty(name = "coreSize", value = "15")
        }
)
public BigDecimal hystrixCalculateAddCacheResult(BigDecimal number1, BigDecimal number2) {
    logger.info("测试使用请求缓存......");
    String url = String.format("http://127.0.0.1:3030/providerCalculateAdd?number1=%s&number2=%s", number1, number2);
    BigDecimal result = restTemplate.getForObject(url, BigDecimal.class);
    return result;
}

 /**
  * 生成缓存键
  */
 public String getCacheKey(BigDecimal number1, BigDecimal number2) {
     return "cache-" + number1 + "-" + number2;
 }
重新启动hystrix-demo服务,使用postman调用接口

在这里插入图片描述
控制器里面方法hystrixCalculateAddCacheResult被调用了3次,但是实际只执行了1次,后面的两次都是从缓存中获取的结果。
在这里插入图片描述

5.2 移除请求缓存
Hystrix也可以根据缓存键移除对应的请求缓存。

HystrixDemoController添加移除缓存的代码
/**
  * 移除请求缓存
  */
 @GetMapping("/hystrixCalculateAddCacheRemove")
 public BigDecimal hystrixCalculateAddCacheRemove(BigDecimal number1, BigDecimal number2) {
     BigDecimal resultA = hystrixDemoService.hystrixCalculateAddCacheResult(number1, number2);
     BigDecimal resultB = hystrixDemoService.removeHystrixCalculateAddCache(number1, number2);
     BigDecimal resultC = hystrixDemoService.hystrixCalculateAddCacheResult(number1, number2);

     return resultC;
 }
hystrixDemoService添加移除缓存的代码
/**
 * 生成缓存键
 */
public String getCacheKey(BigDecimal number1, BigDecimal number2) {
    return "cache-" + number1 + "-" + number2;
}

@CacheRemove(commandKey = "CacheCommandKey", cacheKeyMethod = "getCacheKey")
@HystrixCommand(
        groupKey = "RemoveCacheGroupKey",
        commandKey = "RemoveCacheCommandKey",
        threadPoolKey = "RemoveCacheThreadPoolKey",
        commandProperties = {
                @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "5000"),
                @HystrixProperty(name = "execution.timeout.enabled", value = "true"),
                @HystrixProperty(name = "execution.isolation.strategy", value = "THREAD")
        },
        threadPoolProperties = {
                @HystrixProperty(name = "coreSize", value = "15")
        }
)
public BigDecimal removeHystrixCalculateAddCache(BigDecimal number1, BigDecimal number2) {
    logger.info("测试移除请求缓存......");
    String url = String.format("http://127.0.0.1:3030/providerCalculateAdd?number1=%s&number2=%s", number1, number2);
    BigDecimal result = restTemplate.getForObject(url, BigDecimal.class);
    return result;
}
重新启动hystrix-demo服务,通过postman调用清除缓存的接口

在这里插入图片描述

控制台打印日志说明调用了两次添加请求缓存的方法,证明请求缓存移除成功。

在这里插入图片描述

6 Spring Cloud Hystrix Dashboard 监控
spring cloud 提供了hystrix dashboard组件,便于我们监控hystrix的运行情况。
6.1 修改hystrix-demo项目暴露hystrix.stream端点
pom文件添加Spring Boot Actuator 组件,方便提供 Hystrix 监控数据
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
application.properties配置暴露 hystrix.stream 端点
management.endpoints.web.exposure.include=hystrix.stream
重新启动hystrix-demo项目,浏览器访问下面的url,可以看到hystrx的监控数据:http://127.0.0.1:3020/actuator/hystrix.stream

在这里插入图片描述

6.2 搭建 Hystrix Dashboard项目
创建pom文件,引入hystrix dashboard依赖
<parent>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-parent</artifactId>
   <version>2.3.0.RELEASE</version>
   <relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>hystrix-dashboard-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>hystrix-dashboard-demo</name>
<description>Demo project for Spring Boot</description>

<properties>
   <java.version>1.8</java.version>
   <spring-cloud.version>Hoxton.SR9</spring-cloud.version>
</properties>

<dependencyManagement>
   <dependencies>
       <dependency>
           <groupId>org.springframework.cloud</groupId>
           <artifactId>spring-cloud-dependencies</artifactId>
           <version>${spring-cloud.version}</version>
           <type>pom</type>
           <scope>import</scope>
       </dependency>
   </dependencies>
</dependencyManagement>

<dependencies>
   <dependency>
       <groupId>org.springframework.cloud</groupId>
       <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
   </dependency>

   <dependency>
       <groupId>org.springframework.cloud</groupId>
       <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
   </dependency>

   <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-web</artifactId>
   </dependency>

   <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-test</artifactId>
       <scope>test</scope>
   </dependency>
</dependencies>
创建bootstrap.yml文件,增加eureka和hystrix dashboard白名单配置
spring:
  application:
    name: hystrix-dashboard-demo
  profiles:
    active: dev
server:
  port: 3040
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8090/eureka/
  instance:
    lease-expiration-duration-in-seconds: 30
    lease-renewal-interval-in-seconds: 10
    prefer-ip-address: true
    instance-id: ${spring.cloud.client.ip-address}:${server.port}
# 添加ip白名单
hystrix:
  dashboard:
    proxy-stream-allow-list: 127.0.0.1
Application启动类添加@EnableHystrixDashboard,开启Hystrix Dashboard 功能
@SpringBootApplication
@EnableDiscoveryClient
@EnableHystrixDashboard
public class HystrixDashboardDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(HystrixDashboardDemoApplication.class, args);
    }

}
启动hystrix-dashboard-demo项目,在浏览器中输入 http://127.0.0.1:3040/hystrix,进入 Hystrix Dashboard 首页,如下图所示:

在这里插入图片描述
在文本框中输入地址:http://127.0.0.1:3020/actuator/hystrix.stream,表示hystrix dashboard会展示之前hystrix-demo项目的hystrix监控数据。

在这里插入图片描述
点击「Monitor Stream」按钮,展示 Hystrix 监控数据。
在这里插入图片描述

7 Spring Cloud Hystrix Dashboard+Turbine聚合监控
在实际项目中,一个服务我们会部署多个实例,同时服务肯定是多个,如果直接使用 Hystrix Dashboard 搭配 hystrix.stream 端点的方式来监控 Hystrix 数据,只能一个一个服务实例进行查看,显然非常不方便。很多时候,我们希望看到所有服务,并且将相同服务实例的 Hystrix 监控数据进行聚合展示。因此,Netflix 提供了 spring-cloud-netflix-turbine 组件,Turbine 从注册中心获取所有服务实例的地址,从而抓取它们的 hystrix.stream 端点的 Hystrix 监控数据,进行聚合计算;而Hystrix Dashboard直接展示Turbine 聚合计算好的数据。整体架构图如下:

在这里插入图片描述

7.1 启动多个hyxstrix-demo项目实例

按照下图所示设置hystrix-demo服务的端口号,然后再执行HystrixDemoApplication启动该项目。
在这里插入图片描述
然后,我们会在eureka注册中心看到两个hystrix-demo实例
在这里插入图片描述

7.2 搭建 Hystrix Dashboard + Turbine

基于「6.2小节Hystrix Dashboard 项目搭建」修改hystrix-dashboard-demo项目

pom文件添加turbine依赖

<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-netflix-turbine</artifactId>
</dependency>

bootstrap.yml添加turbine配置

# Turbine 配置项,对应 TurbineProperties 配置类
turbine:
  # 配置需要 Turbine 聚合的服务名;如果有多个,使用逗号分隔。
  app-config: hystrix-demo
  # 服务是否以 host + port 进行区分,默认为 true
  combine-host-port: true
  # 指定集群名,设置为 `default` 表示默认集群
  cluster-name-expression: new String ('default')

Application启动类增加@EnableTurbine注解

@SpringBootApplication
@EnableDiscoveryClient
@EnableHystrixDashboard
@EnableTurbine
public class HystrixDashboardDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(HystrixDashboardDemoApplication.class, args);
    }

}

执行HystrixDashboardDemoApplication启动Hystrix Dashboard+Turbine

<1> 使用浏览器访问地址:http://127.0.0.1:3020/hystrixCalculateAddThread?number1=10&number2=20,执行 Hystrix Command 的调用,从而产生 Hystrix 监控数据。相同方式再调用3025端口对应的的hystrixCalculateAddThread方法。
在这里插入图片描述
在这里插入图片描述

<2> 使用浏览器访问地址:http://127.0.0.1:3040/turbine.stream,可以获得 Turbine 聚合 Hystrix 监控数据。
在这里插入图片描述
使用浏览器访问地址:http://127.0.0.1:3040/hystrix,进入Hystrix Dashboard 首页。在文本框中输入地址:http://127.0.0.1:3040/turbine.stream表示抓取Turbine聚合Hystrix监控的数据。

在这里插入图片描述
在这里插入图片描述
至此,Spring Cloud 服务容错 Hystrix 入门分享完毕

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值