文章目录
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注解参数使用说明
参数名称 | 说明 |
---|---|
groupKey | Hystrix Command 命令分组键,默认未配置情况下使用方法所在类名 |
commandKey | Hystrix Command 命令键,默认未配置情况下使用方法名 |
threadPoolKey | 线程池名,用于划分不同的线程池,进行资源隔离。默认未配置情况下,相同 groupKey 的 Hystrix Command 使用同一个线程池;在配置情况下,相同 groupKey + threadPoolKey 使用同一个线程池。 |
fallbackMethod | 服务降级的处理方法 |
thread.timeoutInMilliseconds | 请求超时时长,单位毫秒 |
execution.timeout.enabled | 是否开启超时设置,默认为true |
execution.isolation.strategy | 资源隔离实现策略,线程池隔离:THREAD,信号量隔离:SEMAPHORE |
coreSize | 核心线程数 |
@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方法线程数
使用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 入门分享完毕