Hystrix cache 简介
hystrix给我们提供了缓存功能,支持将一个请求结果缓存起来,下一个具有相同key的请求将直接从缓存中取出结果,减少请求开销。
要使用hystrix cache功能需要实现以下两步
1、要求是重写 getCacheKey() 方法,用来构造cache key;
2、要求是构建context,如果请求B要用到请求A的结果缓存,A和B必须同处一个context。
HystrixRequestContext
通过 HystrixRequestContext.initializeContext() 和 context.shutdown()可以构建一个context,这两条语句间的所有请求都处于同一个context。
示例
package com.jijs.cache.test;
import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandGroupKey;
import com.netflix.hystrix.strategy.concurrency.HystrixRequestContext;
/**
* cache只有同在一个context中才生效
* 通过HystrixRequestContext.initializeContext()初始化context,通过shutdown()关闭context
*/
public class HystrixCommandCacheTest extends HystrixCommand<Integer> {
private final String key;
private final int value;
protected HystrixCommandCacheTest(String key, int value) {
super(HystrixCommandGroupKey.Factory.asKey("RequestCacheCommandGroup"));
this.key = key;
this.value = value;
}
// 返回结果是cache的value
@Override
protected Integer run() {
System.out.println("执行计算["+key+":"+value+"]");
return value * 2;
}
// 构建cache的key
@Override
protected String getCacheKey() {
return key + value;
}
public static void main(String[] args) {
HystrixRequestContext context = HystrixRequestContext.initializeContext();
try {
HystrixCommandCacheTest cache2a = new HystrixCommandCacheTest("cache1",2);
HystrixCommandCacheTest cache2b = new HystrixCommandCacheTest("cache1",2);
HystrixCommandCacheTest cache2c = new HystrixCommandCacheTest("cache2",2);
System.out.println("结果:" + cache2a.execute() + ";是否从缓存取值:" + cache2a.isResponseFromCache());
System.out.println("结果:" + cache2b.execute() + ";是否从缓存取值:" + cache2b.isResponseFromCache());
System.out.println("结果:" + cache2c.execute() + ";是否从缓存取值:" + cache2c.isResponseFromCache());
} finally {
context.shutdown();
}
}
}
执行结果
执行计算[cache1:2]
结果:4;是否从缓存取值:false
结果:4;是否从缓存取值:true
执行计算[cache2:2]
结果:4;是否从缓存取值:false
从结果中可以看出 cache2a 、 cache2b 和 cache2c 都在同一个 HystrixRequestContext 上下文中。
1、当执行 cache2a 时,没有缓存,则把cache2a的执行结果缓存起来。
2、再执行 cache2b 时,发现 缓存中已经存在,则从缓存中获取,没有调用 run() 方法去计算结果。
3、执行 cache2c 时,缓存中没有该数据,则调用run() 方法进行计算返回,并把该计算结果缓存起来。
HystrixRequestContext 源码分析
public class HystrixRequestContext implements Closeable {
private static ThreadLocal<HystrixRequestContext> requestVariables = new ThreadLocal<HystrixRequestContext>();
public static boolean isCurrentThreadInitialized() {
HystrixRequestContext context = requestVariables.get();
return context != null && context.state != null;
}
public static HystrixRequestContext getContextForCurrentThread() {
HystrixRequestContext context = requestVariables.get();
if (context != null && context.state != null) {
return context;
} else {
return null;
}
}
public static void setContextOnCurrentThread(HystrixRequestContext state) {
requestVariables.set(state);
}
public static HystrixRequestContext initializeContext() {
HystrixRequestContext state = new HystrixRequestContext();
requestVariables.set(state);
return state;
}
ConcurrentHashMap<HystrixRequestVariableDefault<?>, HystrixRequestVariableDefault.LazyInitializer<?>> state = new ConcurrentHashMap<HystrixRequestVariableDefault<?>, HystrixRequestVariableDefault.LazyInitializer<?>>();
private HystrixRequestContext() {
}
public void shutdown() {
if (state != null) {
for (HystrixRequestVariableDefault<?> v : state.keySet()) {
try {
HystrixRequestVariableDefault.remove(this, v);
} catch (Throwable t) {
HystrixRequestVariableDefault.logger.error("Error in shutdown, will continue with shutdown of other variables", t);
}
}
state = null;
}
}
public void close() {
shutdown();
}
}
从代码中可以看出,HystrixRequestContext 使用了 ThreadLocal 进行存储。也就是说,缓存的范围是线程隔离的,当前线程缓存的数据,只能在当前线程中使用。线程销毁则缓存自动失效。
如果执行 HystrixRequestContext.shutdown() 或者 close() 方法时,缓存也会失效。
缓存核心源码分析
1、首先调用 isRequestCachingEnabled() 方法判断是否开启 cache 功能。
2、如果开启,则通过 我们自己重写的 getCacheKey() 方法获取,要冲缓冲获取的 cacheKey。
public abstract class HystrixCommand<R> extends AbstractCommand<R>
implements HystrixExecutable<R>, HystrixInvokableInfo<R>, HystrixObservable<R>
从代码中可以看到 HystrixCommand 实现了 AbstractCommand 类,所以 getCacheKey() 就是我们重写的方法。
3、从 requestCache.get() 方法中获取缓存数据。
判断是否开启缓存
protected boolean isRequestCachingEnabled() {
return properties.requestCacheEnabled().get() && getCacheKey() != null;
}
首先判断 HystrixCommandProperties.Setter().withRequestCacheEnabled(boolean value) 是否指定了开启缓存
然后在判断 是否重写了 getCacheKey() 方法,并且重写 getCacheKey() 方法返回值不能为 null。
获取缓存数据
从 HystrixRequestCache中获取缓存数据, 调用 requestCache.get() 方法获取。
HystrixRequestCache 类中使用了 ConcurrentHashMap ,根据 key、value 进行缓存执行的结果。
网友评论