dubbo结果缓存机制
此文已由作者赵计刚授权网易云社区发布。
欢迎访问网易云社区,了解更多网易技术产品运营经验。
dubbo提供了三种结果缓存机制:
lru:基于最近最少使用原则删除多余缓存,保持最热的数据被缓存
threadlocal:当前线程缓存
jcache:可以桥接各种缓存实现
一、使用方式
1 <dubbo:reference id="demoService" check="false" interface="com.alibaba.dubbo.demo.DemoService">
2 <dubbo:method name="sayHello" timeout="60000" cache="lru"/>
3 </dubbo:reference>
添加cache配置。
注意:dubbo结果缓存有一个bug,https://github.com/alibaba/dubbo/issues/1362,当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,...,arg4
19 String key = StringUtils.toArgumentString(invocation.getArguments());
20 // 获取缓存value
21 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获取具体的Cache
11 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<String, Cache> caches = new ConcurrentHashMap<String, Cache>();
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<Object, Object> store;
8
9 public LruCache(URL url) {
10 final int max = url.getParameter("cache.size", 1000);
11 this.store = new LRUCache<Object, Object>(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<K, V> extends LinkedHashMap<K, V> {
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 eldest
26 * @return
27 */
28 @Override
29 protected boolean removeEldestEntry(java.util.Map.Entry<K, V> eldest) {
30 return size() > maxCapacity;
31 }
32
33 @Override
34 public V get(Object key) {
35 try {
36 lock.lock();
37 return super.get(key);
38 } finally {
39 lock.unlock();
40 }
41 }
42
43 @Override
44 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 @Override
54 public V remove(Object key) {
55 try {
56 lock.lock();
57 return super.remove(key);
58 } finally {
59 lock.unlock();
60 }
61 }
62
63 @Override
64 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实现的,具体源码分析见 https://yikun.github.io/2015/04/02/Java-LinkedHashMap%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86%E5%8F%8A%E5%AE%9E%E7%8E%B0/ http://wiki.jikexueyuan.com/project/java-collection/linkedhashmap.html)
三、ThreadLocal缓存源码解析
根据文章开头提到的bug,cache=""只能配置在服务级别。
1 <dubbo:reference id="demoService" check="false" interface="com.alibaba.dubbo.demo.DemoService" cache="threadlocal"/>
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<Map<Object, Object>> store;
9
10 public ThreadLocalCache(URL url) {
11 this.store = new ThreadLocal<Map<Object, Object>>() {
12 @Override
13 protected Map<Object, Object> initialValue() {
14 return new HashMap<Object, Object>();
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。
相关文章:
【推荐】 从golang的垃圾回收说起(上篇)
【推荐】 Kylin存储和查询的分片问题
【推荐】 手把手带你打造一个 Android 热修复框架(上篇)
dubbo结果缓存机制的更多相关文章
- 第十五章 dubbo结果缓存机制
dubbo提供了三种结果缓存机制: lru:基于最近最少使用原则删除多余缓存,保持最热的数据被缓存 threadlocal:当前线程缓存 jcache:可以桥接各种缓存实现 一.使用方式 <du ...
- Dubbo剖析-SPI机制
文章要点: 1.什么是SPi 2.Dubbo为什么要实现自己的SPi 3.Dubbo的IOC和AOP 4.Dubbo的Adaptive机制 5.Dubbo动态编译机制 6.Dubbo与Spring的融 ...
- 探究Dubbo的拓展机制: 上
这篇博文是我决心深度学习Dubbo框架时记录的笔记, 主题是Dubbo的拓展点, 下面的几个部分相对来说比较零散, 貌似是不和主题挂钩的 , 并且是一些很冷门的知识点 , 但是它们确实是深入学习Dub ...
- 探究Dubbo的拓展机制: 下
承接上篇, 本篇博文的主题就是认认真真捋一捋, 看一下 Dubbo是如何实现他的IOC / AOP / 以及Dubbo SPI这个拓展点的 总览: 本篇的话总体上分成两部分进行展开 第一点就是 Dub ...
- jdk和dubbo的SPI机制
前言:开闭原则一直是软件开发领域中所追求的,开闭原则中的"开"是指对于组件功能的扩展是开放的,是允许对其进行功能扩展的,“闭”,是指对于原有代码的修改是封闭的,即不应该修改原有的代 ...
- Dubbo的SPI机制与JDK机制的不同及原理分析
从今天开始,将会逐步介绍关于DUbbo的有关知识.首先先简单介绍一下DUbbo的整体概述. 概述 Dubbo是SOA(面向服务架构)服务治理方案的核心框架.用于分布式调用,其重点在于分布式的治理. 简 ...
- 面试常问的dubbo的spi机制到底是什么?
前言 dubbo是一款微服务开发框架,它提供了 RPC通信 与 微服务治理 两大关键能力.作为spring cloud alibaba体系中重要的一部分,随着spring cloud alibaba在 ...
- 【腾讯Bugly干货分享】彻底弄懂 Http 缓存机制 - 基于缓存策略三要素分解法
本文来自于腾讯Bugly公众号(weixinBugly),未经作者同意,请勿转载,原文地址:https://mp.weixin.qq.com/s/qOMO0LIdA47j3RjhbCWUEQ 作者:李 ...
- MyCat源码分析系列之——BufferPool与缓存机制
更多MyCat源码分析,请戳MyCat源码分析系列 BufferPool MyCat的缓冲区采用的是java.nio.ByteBuffer,由BufferPool类统一管理,相关的设置在SystemC ...
随机推荐
- 基于JDK1.7.0_80与JDK1.8.0_66做的分析
JDK1.7中 使用一个Entry数组来存储数据,用key的hashcode取模来决定key会被放到数组里的位置,如果hashcode相同,或者hashcode取模后的结果相同(hash collis ...
- flask之instance_path实例路径
Flask 0.8 introduces instance folders. Flask for a long time made it possible to refer to paths rela ...
- thinkphp <volist>标签中 <if> 判断的写法
thinkphp <volist>标签中 <if> 判断的写法 <volist name="data" id="vo"> & ...
- hibernate中mysql数据交互中文乱码问题
来源于"http://www.cnblogs.com/jiafuwei/p/4423101.html"; 修改hibernate的配置文件hibernate.cfg.xml,在配置 ...
- 机器学习:使用scikit-learn库中的网格搜索调参
一.scikit-learn库中的网格搜索调参 1)网格搜索的目的: 找到最佳分类器及其参数: 2)网格搜索的步骤: 得到原始数据 切分原始数据 创建/调用机器学习算法对象 调用并实例化scikit- ...
- DCloud-5+Runtime:杂项
ylbtech-DCloud-5+Runtime:杂项 1.返回顶部 2.返回顶部 3.返回顶部 4.返回顶部 5.返回顶部 6.返回顶部 7.返回顶部 8.返回顶部 ...
- JavaEE中的Cookie的基本使用方法
之前一直使用的是统一登录系统,相关的登录由别的部门开发以及维护.但由于最近项目的需要,我们需要自己开发一套简单的登录功能.因此这里就涉及到了一个Cookie的功能.之前也了解过相关的内容,但这次需要独 ...
- python 函数对象、函数嵌套、名称空间与作用域、装饰器
一 函数对象 一 函数是第一类对象,即函数可以当作数据传递 1 可以被引用 2 可以当作参数传递 3 返回值可以是函数 3 可以当作容器类型的元素 二 利用该特性,优雅的取代多分支的if def fo ...
- Oracle Management Packs
http://kerryosborne.oracle-guy.com/2008/10/oracle-management-packs/ There has been quite a bit of co ...
- Latex 多个参考文献的引用
如果在文章中出现连续引用多个参考文献的情况,希望显示的格式为 [1-5,9,12],那么可以如下处理: 在文章的导言区加 \usepackage[square, comma, sort&com ...