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 进行缓存执行的结果。
作者:jijs
链接:https://www.jianshu.com/p/e2d1d319fadd