手记

Hystrix cache 源码分析

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


0人推荐
随时随地看视频
慕课网APP