在分布式环境下,通常压力来自于对依赖服务的调用,因为请求依赖服务的资源需要通过通信实现,这样的依赖方式比起进程内的调用会方式会引起一部分的性能损失,同事HTTP相比于其它高性能的通信协议在速度是没有任何优势,所以它有些类似于对数据库这样的外部资源进行读写操作,在高并发情况下可能会成为系统的瓶颈。
在高并发的场景之下,Hystrix提供了请求缓存功能,我们可以方便的开启和使用请求缓存来优化系统,达到减轻高并发时的请求线程消耗、降低请求响应时间的效果。
开启缓存方式
1.在HystrixCommand、HystrixObservableCommand实现的Hystrix命令中重载getCacheKey()方法来开启请求缓存,该方法不能返回null,返回null不会开启请求缓存功能。
2、使用@CacheResult注解开启请求缓存
实现
新建消费者服务实例
- 创建一个新的Spring Boot工程,命名为:hystrix-cache(随意)
- 编辑pom.xml,主要依赖如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
- 修改application.yml
spring:
application:
name: hystrix-cache
server:
port: 8000
eureka:
client:
service-url:
defaultZone: http://peer1:1111/eureka/
- 编辑应用主类
@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker
public class HystrixApplication {
@Bean
//开启负载均衡
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(HystrixApplication.class, args);
}
}
- 编写controller类
@RestController
@RequestMapping(value = "/cache")
public class CacheController {
@Autowired
private CacheService cacheService;
@GetMapping(path = "/test")
public String testCache() {
HystrixRequestContext.initializeContext();
//第一次会远程调用查询数据,后边两次会查询缓存
cacheService.getUserById("1");
cacheService.getUserById("1");
//删除缓存
cacheService.update("1");
//再次查询,第一次会再次远程调用获取数据,后边两次直接查询缓存
cacheService.getUserById("1");
cacheService.getUserById("1");
cacheService.getUserById("2");
cacheService.getUserById("2");
return "请求成功";
}
}
- 编写service类
@Service
public class CacheService {
@Autowired
RestTemplate restTemplate;
//@CacheResult 用来标记请求命令返回结果应该被缓存
//cacheKeyMethod指定key的函数,Key的生成函数
@CacheResult(cacheKeyMethod = "getCacheKey")
@HystrixCommand(fallbackMethod = "error")
public String getUserById(String id) {
return restTemplate.getForObject("http://hello-client/cache/getUserById?id={1}", String.class,id);
}
//用来让缓存失效,失效的缓存根据定义的key决定
//@CacheRemove注解有commandKey、cacheKeyMethod两个参数
//commandKey属性是必须要指定的,它用来指明需要使用请求缓存的请求命令,只有配置了该属性,Hystrix才能找到正确的请求命令缓存位置。
@CacheRemove(commandKey = "getUserById")
public String update(String id) {
return restTemplate.postForObject("http://hello-client/cache/updateUserById?id={id}", null, String.class,id);
}
public String getCacheKey(String id) {
//使用ID作为缓存的key
return id;
}
public String error(String id) {
return "出错啦出错啦";
}
}
修改服务提供者服务实例
- 添加controller
@RestController
@RequestMapping(value = "/cache")
public class CacheController {
@RequestMapping(value = "/getUserById")
public String getUserById(String id) {
System.out.println("处理消费者请求--获取用户:" + id);
return id;
}
@PostMapping(value = "/updateUserById")
public String updateUserById(String id) {
System.out.println("处理消费者请求--更新用户:" + id);
return id;
}
}
测试
1、启动注册中心、启动服务提供实例、启动刚刚创建的工程
2、分析:
通过刚刚消费者服务新建的controller里面的调用逻辑来看,如果缓存生效的话,应该是下面的执行情况:
1.使用参数“1”发送请求,会远程调用获取数据
2.再次使用参数“1”发送请求,直接从缓存中获取数据
3.然后是清除key为“1”的缓存
4.再次使用参数“1”发送请求,应该会重新执行远程调用获取数据
5.再次使用参数“1”发送请求,直接从缓存中获取数据
6.使用参数“2”发送请求,会远程调用获取数据
7.再次使用参数“2”发送请求,直接从缓存中获取数据
服务提供者实例应该会收到三个请求,下面我们来验证一下
访问:http://localhost:8000/cache/test

从服务提供者控制台打印可以看到结果和我们上面分析的一致。
使用@CacheKey注解实现请求缓存更简单,但是使用@CacheKey注解的时候需要注意,它的优先级比cacheKeyMethod的优先级低,如果已经使用了cacheKeyMethod指定缓存Key的生成函数,那么@CacheKey注解不会生效,示例如下:
@CacheResult
@HystrixCommand(fallbackMethod = "error")
public String getUserById(@CacheKey("id") String id) {
return restTemplate.getForObject("http://hello-client/cache/getUserById?id={1}", String.class,id);
}
网友评论