这篇文章,主要介绍微服务组件之Hystrix实现请求缓存功能。
目录
一、Hystrix组件介绍
1.1、什么是Hystrix
在微服务环境下,服务和服务之间的调用可能会非常的频繁,如果某个微服务的请求非常多,并且这个微服务又无法处理过来,那么此时就可能导致这台服务器出现宕机的情况,一旦出现宕机,那么调用这个微服务的其他微服务系统,也将受到影响,微服务中的请求一直积压,就会导致所有的微服务系统发生崩溃,我们把这种情况称作是:服务雪崩。
为了避免服务雪崩,以及提高微服务的高可用性,于是就出现了Hystrix服务容错组件,Hystrix提供了多种功能,例如:请求缓存、请求合并、线程池隔离、信号量隔离、服务降级、服务熔断、服务监控等等。
- PS:Hystrix英文单词是豪猪的意思。
1.2、Hystrix的功能
Hystrix提供的功能大概有下面这几个:
- 请求缓存:可以根据请求的URI将其返回结果暂时缓存起来,下次请求相同的URI就会从缓存中获取数据。
- 请求合并:可以将客户端多个请求,合并成一个请求,采用批处理的方式调用服务端接口。
- 线程池隔离:将不同的请求采用不同的线程池隔离,这样即使其中一个线程池的请求出现问题,也不会影响其他线程池的请求。
- 信号量隔离:项目中存在太多的线程池会降低性能,因为线程池需要来回切换线程,所以就提出了信号量隔离机制。
- 服务降级:当服务调用过程中出现问题,可以调用备选方法实现快速失败返回。
- 服务熔断:Hystrix中有个熔断器,当其他微服务不可用的时候,会开启熔断器,此时所有的调用请求都将被阻断,直接快速返回失败。
- 服务监控:Hystrix提供了一个dashboard控制面板,可以监控并查看请求的调用情况。
二、Hystrix实现请求缓存(了解)
Hystrix提供的请求缓存是一个本地缓存,在分布式环境下不适用,并且实际开发中,也不会使用hystrix的请求缓存功能,因为这个功能太多余了,Hystrix的请求缓存只能够在同一个request请求作用域下才生效,下一次的request请求,会将上一次request的请求缓存给清空。下面介绍如何使用Hystrix实现请求缓存功能。
2.1、请求缓存介绍
hystrix请求缓存是指:在同一个request请求之下,多次调用其他微服务的时候,会将第一次调用结果缓存起来,然后之后每次都是从缓存里面获取数据结果。
可以这么理解,hystrix缓存的是service层方法的返回结果,当在controller层中同一个HTTP请求里面,多次调用service层方法的时候,此时会从缓存中获取数据。
什么叫做同一次HTTP请求呢???
- 一个用户访问地址【/api/demo】,此时请求进入到controller层,然后我们在controller层方法中,调用了两次service层的方法,那么此时第二次调用service层的方法返回值,将从hystrix缓存里面获取。
- 在上面这个案例中,就只在一次HTTP请求里面,如果用户访问了两次【/api/demo】,那么此时就不是同一次HTTP请求了,第一次的缓存数据将会被第二次的缓存数据覆盖掉。
- 当然,前提是这个方法开启了请求缓存功能。
2.2、引入依赖
<!-- hystrix 依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
2.3、服务层代码
在service层中,可以有两种方式指定需要开启请求缓存的功能,分别是:
- 第一种:使用【@HystrixCommand】注解、【@CacheResult】注解以及@CacheResult注解中的cacheKeyMethod属性。
- 第二种:使用【@HystrixCommand】注解、【@CacheResult】注解、【】注解。
两种方式的区别在于:设置缓存key的方式不同,一个是通过注解指定缓存key,一个是通过自定义方法。
(1)第一种缓存实现方式
package com.gitcode.hystrix.service;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.cache.annotation.CacheResult;
import org.springframework.stereotype.Service;
import java.security.SecureRandom;
/**
* @version 1.0.0
* @Date: 2023/4/7 20:54
* @Copyright (C) ZhuYouBin
* @Description: hystrix 请求缓存案例
*/
@Service
public class HystrixCacheService {
// 将当前方法的返回保持到缓存里面
@CacheResult(cacheKeyMethod = "getCacheKey")
@HystrixCommand(commandKey = "requestCacheKey") // 声明当前方法需要执行 hystrix 命令
public String requestCache(String id) {
// TODO 这里可以调用其他的微服务完成一些业务逻辑功能
// 为了简单些,我这里就生产一个随机数作为返回值
SecureRandom random = new SecureRandom();
return random.nextInt() + "";
}
/**
* 定义获取 缓存key 的方法
* <p>
* 注意:获取缓存key的方法参数必须和开启请求缓存方法的参数列表相同
* </p>
*/
public String getCacheKey(String id) {
return id;
}
}
(2)第二种缓存实现方式
// 将当前方法的返回保持到缓存里面
@CacheResult
@HystrixCommand(commandKey = "requestCacheKey") // 声明当前方法需要执行 hystrix 命令
public String requestCache(@CacheKey String id) { // 将当前参数作为缓存key
// TODO 这里可以调用其他的微服务完成一些业务逻辑功能
// 为了简单些,我这里就生产一个随机数作为返回值
SecureRandom random = new SecureRandom();
return random.nextInt() + "";
}
注意:如果没有指定缓存key,那么此时hystrix会将当前方法中的所有参数作为缓存的key。
2.4、控制层代码
因为hystrix的请求缓存必须是在同一次请求里面,所以必须使用hystrix提供的【HystrixRequestContext】类,初始化请求上下文,并且在每次请求结束之后,需要清除缓存。
如果每一次都在controller里面使用HystrixRequestContext类初始化上下文,那么就会出现很多的重复代码,所以为了简化代码,一般可以在一个过滤器里面使用HystrixRequestContext类完成上下文的初始化和缓存清除功能。
- 注意:如果没有使用HystrixRequestContext类,那么会抛出异常。
Request caching is not available. Maybe you need to initialize the HystrixRequestContext?
(1)控制层使用HystrixRequestContext
package com.gitcode.hystrix.controller;
import com.gitcode.hystrix.service.HystrixCacheService;
import com.netflix.hystrix.strategy.concurrency.HystrixRequestContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @version 1.0.0
* @Date: 2023/4/7 21:09
* @Copyright (C) ZhuYouBin
* @Description:
*/
@RestController
@RequestMapping("/api/hystrix")
public class HystrixController {
@Autowired
private HystrixCacheService hystrixCacheService;
@GetMapping("/cache")
public String hystrixCache() {
// 1、开启hystrix请求上下文
HystrixRequestContext context = HystrixRequestContext.initializeContext();
// 2、TODO 在这之间,就可以多次调用service层代码
String rtn = hystrixCacheService.requestCache("1001");
System.out.println("第一次调用返回结果: " + rtn);
// 参数相同的情况下,第二次调用的返回值是从缓存获取的,所以必定和第一次的返回值相同
rtn = hystrixCacheService.requestCache("1001");
System.out.println("第二次调用返回结果: " + rtn);
// 注意: 入参不同
rtn = hystrixCacheService.requestCache("1002");
System.out.println("第三次调用返回结果: " + rtn);
// 3、清除缓存
context.close();
return "success";
}
}
(2)过滤器使用HystrixRequestContext
package com.gitcode.hystrix.filter;
import com.netflix.hystrix.strategy.concurrency.HystrixRequestContext;
import javax.servlet.*;
import java.io.IOException;
/**
* @version 1.0.0
* @Date: 2023/4/7 21:23
* @Copyright (C) ZhuYouBin
* @Description: hystrix请求缓存过滤器
*/
public class HystrixCacheFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HystrixRequestContext context = null;
try {
// 开启 hystrix 请求上下文
context = HystrixRequestContext.initializeContext();
// 继续执行后面代码
chain.doFilter(request, response);
} finally {
if (null != context) {
// 每次请求结束,清除缓存
context.close();
}
}
}
}
- SpringBoot将这个过滤器注入IOC容器即可。
package com.gitcode.hystrix.config;
import com.gitcode.hystrix.filter.HystrixCacheFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
/**
* @version 1.0.0
* @Date: 2023/4/7 21:40
* @Copyright (C) ZhuYouBin
* @Description:
*/
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<HystrixCacheFilter> hystrixCacheFilter() {
FilterRegistrationBean<HystrixCacheFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(new HystrixCacheFilter());
registration.addUrlPatterns("/*");
registration.setName("HystrixCacheFilter");
// 设置优先级别
registration.setOrder(Ordered.LOWEST_PRECEDENCE);
return registration;
}
}
2.5、开启hystrix功能
在启动类上面,需要使用【@EnableCircuitBreaker】注解或者【@EnableHystrix】注解,开启hystrix熔断器功能,否则hystrix将不生效。两个注解的作用是相同的,@EnableHystrix注解就已经包含了@EnableCircuitBreaker注解,但是官方使用的是@EnableCircuitBreaker注解,所以我们也就按照官方的用法,直接使用@EnableCircuitBreaker注解开启功能即可。
2.6、缓存删除
hystrix也提供了删除缓存的注解@CacheRemove,当进行一些新增、删除、更新操作的时候,此时缓存中的数据就可能不是最新的了,所以这个时候就需要将缓存中的数据删除。使用【@CacheRemove】注解,同时指定【commandKey】属性,这就是告诉hystrix需要删除哪个命令下的所有缓存。
- 注意:这里的commanKey需要和请求缓存中@HystrixCommand注解中指定的commanKey属性相同。
/**
* 删除 commandKey 等于 requestCacheKey 中的所有缓存
*/
@CacheRemove(commandKey = "requestCacheKey")
public void updateData() {
// TODO 做一些更新操作
}
到此,Hystrix实现请求缓存就介绍完啦。
综上,这篇文章结束了,主要介绍微服务组件之Hystrix实现请求缓存功能。