博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
dubbo结果缓存机制
阅读量:4329 次
发布时间:2019-06-06

本文共 7813 字,大约阅读时间需要 26 分钟。

此文已由作者赵计刚授权网易云社区发布。

欢迎访问,了解更多网易技术产品运营经验。

dubbo提供了三种结果缓存机制:

  • lru:基于最近最少使用原则删除多余缓存,保持最热的数据被缓存

  • threadlocal:当前线程缓存

  • jcache:可以桥接各种缓存实现

一、使用方式

1     
2         
3     

添加cache配置。

注意:dubbo结果缓存有一个bug,,当cache="xxx"配置在服务级别时,没有问题,当配置成方法级别的时候,不管怎么配置,都睡使用LruCache。

 

二、LRU缓存源码解析

  1 /** 2  * CacheFilter 3  * 配置了cache配置才会加载CacheFilter 4  */ 5 @Activate(group = {Constants.CONSUMER, Constants.PROVIDER}, value = Constants.CACHE_KEY) 6 public class CacheFilter implements Filter { 7     private CacheFactory cacheFactory; 8  9     public void setCacheFactory(CacheFactory cacheFactory) {10         this.cacheFactory = cacheFactory;11     }12 13     public Result invoke(Invoker
 invoker, Invocation invocation) throws RpcException {14         if (cacheFactory != null && ConfigUtils.isNotEmpty(invoker.getUrl().getMethodParameter(invocation.getMethodName(), Constants.CACHE_KEY))) {15             // 使用CacheFactory$Adaptive获取具体的CacheFactory,然后再使用具体的CacheFactory获取具体的Cache对象16             Cache cache = cacheFactory.getCache(invoker.getUrl().addParameter(Constants.METHOD_KEY, invocation.getMethodName()));17             if (cache != null) {18                 // 缓存对象的key为arg1,arg2,arg3,...,arg419                 String key = StringUtils.toArgumentString(invocation.getArguments());20                 // 获取缓存value21                 Object value = cache.get(key);22                 if (value != null) {23                     return new RpcResult(value);24                 }25                 Result result = invoker.invoke(invocation);26                 // 响应结果没有exception信息,则将相应结果的值塞入缓存27                 if (!result.hasException()) {28                     cache.put(key, result.getValue());29                 }30                 return result;31             }32         }33         return invoker.invoke(invocation);34     }35 }

从@Activate(group = {Constants.CONSUMER, Constants.PROVIDER}, value = Constants.CACHE_KEY)中我们可以看出,consumer端或provider端配置了cache="xxx",则会走该CacheFilter。

首先获取具体Cache实例:CacheFilter中的cacheFactory属性是CacheFactory$Adaptive实例。

 1 public class CacheFactory$Adaptive implements com.alibaba.dubbo.cache.CacheFactory { 2     public com.alibaba.dubbo.cache.Cache getCache(com.alibaba.dubbo.common.URL arg0) { 3         if (arg0 == null) throw new IllegalArgumentException("url == null"); 4         com.alibaba.dubbo.common.URL url = arg0; 5         String extName = url.getParameter("cache", "lru"); 6         if (extName == null) 7             throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.cache.CacheFactory) name from url(" + url.toString() + ") use keys([cache])"); 8         // 获取具体的CacheFactory 9         com.alibaba.dubbo.cache.CacheFactory extension = (com.alibaba.dubbo.cache.CacheFactory) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.cache.CacheFactory.class).getExtension(extName);10         // 使用具体的CacheFactory获取具体的Cache11         return extension.getCache(arg0);12     }13 }

这里extName使我们配置的lru,如果不配置,默认也是lru。这里获取到的具体的CacheFactory是LruCacheFactory。

 1 @SPI("lru") 2 public interface CacheFactory { 3     @Adaptive("cache") 4     Cache getCache(URL url); 5 } 6  7 public abstract class AbstractCacheFactory implements CacheFactory { 8     private final ConcurrentMap
 caches = new ConcurrentHashMap
(); 9 10     public Cache getCache(URL url) {11         String key = url.toFullString();12         Cache cache = caches.get(key);13         if (cache == null) {14             caches.put(key, createCache(url));15             cache = caches.get(key);16         }17         return cache;18     }19 20     protected abstract Cache createCache(URL url);21 }22 23 public class LruCacheFactory extends AbstractCacheFactory {24     protected Cache createCache(URL url) {25         return new LruCache(url);26     }27 }

调用LruCacheFactory.getCache(URL url)方法,实际上调用的是其父类AbstractCacheFactory的方法。逻辑是:创建一个LruCache实例,之后存储在ConcurrentMap<String, Cache> caches中,key为url.toFullString()。

再来看LruCache的创建:

 1 public interface Cache { 2     void put(Object key, Object value); 3     Object get(Object key); 4 } 5  6 public class LruCache implements Cache { 7     private final Map
 store; 8  9     public LruCache(URL url) {10         final int max = url.getParameter("cache.size", 1000);11         this.store = new LRUCache
(max);12     }13 14     public void put(Object key, Object value) {15         store.put(key, value);16     }17 18     public Object get(Object key) {19         return store.get(key);20     }21 }

默认缓存存储的最大个数为1000个。之后创建了一个LRUCache对象。

 1 public class LRUCache
 extends LinkedHashMap
 { 2     private static final long serialVersionUID = -5167631809472116969L; 3  4     private static final float DEFAULT_LOAD_FACTOR = 0.75f; 5  6     private static final int DEFAULT_MAX_CAPACITY = 1000; 7     private final Lock lock = new ReentrantLock(); 8     private volatile int maxCapacity; 9 10     public LRUCache(int maxCapacity) {11         /**12          * 注意:13          * LinkedHashMap 维护着一个运行于所有Entry的双向链表:此链表定义了迭代顺序,该迭代顺序可以是插入顺序或者是访问顺序14          * 而真正存储的数据结构还是其父类HashMap的那个Entry[]数组,上述的双向链表仅用于维护迭代顺序(帮助实现lru算法等)15          *16          * LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder)17          * 第三个参数accessOrder:false(插入顺序),true(访问顺序)18          */19         super(16, DEFAULT_LOAD_FACTOR, true);20         this.maxCapacity = maxCapacity;21     }22 23     /**24      * 是否需要删除最老的数据(即最近没有被访问的数据)25      * @param eldest26      * @return27      */28     @Override29     protected boolean removeEldestEntry(java.util.Map.Entry
 eldest) {30         return size() > maxCapacity;31     }32 33     @Override34     public V get(Object key) {35         try {36             lock.lock();37             return super.get(key);38         } finally {39             lock.unlock();40         }41     }42 43     @Override44     public V put(K key, V value) {45         try {46             lock.lock();47             return super.put(key, value);48         } finally {49             lock.unlock();50         }51     }52 53     @Override54     public V remove(Object key) {55         try {56             lock.lock();57             return super.remove(key);58         } finally {59             lock.unlock();60         }61     }62 63     @Override64     public int size() {65         try {66             lock.lock();67             return super.size();68         } finally {69             lock.unlock();70         }71     }72     ...73 }

注意:

  • LinkedHashMap维护着一个运行于所有Entry的双向链表:此链表定义了迭代顺序,该迭代顺序可以是插入顺序或者是访问顺序(真正存储的数据结构还是其父类HashMap的那个Entry[]数组,上述的双向链表仅用于维护迭代顺序)

  • 当指定了LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder)第三个参数accessOrder=true时,每次执行get(Object key)时,获取出来的Entry都会被放到尾节点,也就是说双向链表的header节点是最久以前访问的,当执行put(Object key, Object value)的时候,就执行removeEldestEntry(java.util.Map.Entry<K, V> eldest)来判断是否需要删除这个header节点。(这些是LinkedHashMap实现的,具体源码分析见   )

 

三、ThreadLocal缓存源码解析

根据文章开头提到的bug,cache=""只能配置在服务级别。

1 public class ThreadLocalCacheFactory extends AbstractCacheFactory { 2     protected Cache createCache(URL url) { 3         return new ThreadLocalCache(url); 4     } 5 } 6  7 public class ThreadLocalCache implements Cache { 8     private final ThreadLocal
> store; 9 10     public ThreadLocalCache(URL url) {11         this.store = new ThreadLocal
>() {12             @Override13             protected Map
 initialValue() {14                 return new HashMap
();15             }16         };17     }18 19     public void put(Object key, Object value) {20         store.get().put(key, value);21     }22 23     public Object get(Object key) {24         return store.get().get(key);25     }26 }

ThreadLocalCache的实现是HashMap。

相关文章:

【推荐】 
【推荐】 
【推荐】 

转载于:https://www.cnblogs.com/163yun/p/9970167.html

你可能感兴趣的文章
KD树
查看>>
VsVim - Shortcut Key (快捷键)
查看>>
C++练习 | 模板与泛式编程练习(1)
查看>>
HDU5447 Good Numbers
查看>>
08.CXF发布WebService(Java项目)
查看>>
java-集合框架
查看>>
RTMP
查看>>
求一个数的整数次方
查看>>
点云PCL中小细节
查看>>
铁路信号基础
查看>>
RobotFramework自动化2-自定义关键字
查看>>
[置顶] 【cocos2d-x入门实战】微信飞机大战之三:飞机要起飞了
查看>>
BABOK - 需求分析(Requirements Analysis)概述
查看>>
第43条:掌握GCD及操作队列的使用时机
查看>>
Windows autoKeras的下载与安装连接
查看>>
CMU Bomblab 答案
查看>>
微信支付之异步通知签名错误
查看>>
2016 - 1 -17 GCD学习总结
查看>>
linux安装php-redis扩展(转)
查看>>
Vue集成微信开发趟坑:公众号以及JSSDK相关
查看>>