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

  • lru:基于最近最少使用原则删除多余缓存,保持最热的数据被缓存
  • threadlocal:当前线程缓存
  • jcache:可以桥接各种缓存实现

一、使用方式

     <dubbo:reference id="demoService" check="false" interface="com.alibaba.dubbo.demo.DemoService">
<dubbo:method name="sayHello" timeout="60000" cache="lru"/>
</dubbo:reference>

添加cache配置。

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

二、LRU缓存源码解析

 /**
* CacheFilter
* 配置了cache配置才会加载CacheFilter
*/
@Activate(group = {Constants.CONSUMER, Constants.PROVIDER}, value = Constants.CACHE_KEY)
public class CacheFilter implements Filter {
private CacheFactory cacheFactory; public void setCacheFactory(CacheFactory cacheFactory) {
this.cacheFactory = cacheFactory;
} public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
if (cacheFactory != null && ConfigUtils.isNotEmpty(invoker.getUrl().getMethodParameter(invocation.getMethodName(), Constants.CACHE_KEY))) {
// 使用CacheFactory$Adaptive获取具体的CacheFactory,然后再使用具体的CacheFactory获取具体的Cache对象
Cache cache = cacheFactory.getCache(invoker.getUrl().addParameter(Constants.METHOD_KEY, invocation.getMethodName()));
if (cache != null) {
// 缓存对象的key为arg1,arg2,arg3,...,arg4
String key = StringUtils.toArgumentString(invocation.getArguments());
// 获取缓存value
Object value = cache.get(key);
if (value != null) {
return new RpcResult(value);
}
Result result = invoker.invoke(invocation);
// 响应结果没有exception信息,则将相应结果的值塞入缓存
if (!result.hasException()) {
cache.put(key, result.getValue());
}
return result;
}
}
return invoker.invoke(invocation);
}
}

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

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

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

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

 @SPI("lru")
public interface CacheFactory {
@Adaptive("cache")
Cache getCache(URL url);
} public abstract class AbstractCacheFactory implements CacheFactory {
private final ConcurrentMap<String, Cache> caches = new ConcurrentHashMap<String, Cache>(); public Cache getCache(URL url) {
String key = url.toFullString();
Cache cache = caches.get(key);
if (cache == null) {
caches.put(key, createCache(url));
cache = caches.get(key);
}
return cache;
} protected abstract Cache createCache(URL url);
} public class LruCacheFactory extends AbstractCacheFactory {
protected Cache createCache(URL url) {
return new LruCache(url);
}
}

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

再来看LruCache的创建:

 public interface Cache {
void put(Object key, Object value);
Object get(Object key);
} public class LruCache implements Cache {
private final Map<Object, Object> store; public LruCache(URL url) {
final int max = url.getParameter("cache.size", 1000);
this.store = new LRUCache<Object, Object>(max);
} public void put(Object key, Object value) {
store.put(key, value);
} public Object get(Object key) {
return store.get(key);
}
}

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

 public class LRUCache<K, V> extends LinkedHashMap<K, V> {
private static final long serialVersionUID = -5167631809472116969L; private static final float DEFAULT_LOAD_FACTOR = 0.75f; private static final int DEFAULT_MAX_CAPACITY = 1000;
private final Lock lock = new ReentrantLock();
private volatile int maxCapacity; public LRUCache(int maxCapacity) {
/**
* 注意:
* LinkedHashMap 维护着一个运行于所有Entry的双向链表:此链表定义了迭代顺序,该迭代顺序可以是插入顺序或者是访问顺序
* 而真正存储的数据结构还是其父类HashMap的那个Entry[]数组,上述的双向链表仅用于维护迭代顺序(帮助实现lru算法等)
*
* LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder)
* 第三个参数accessOrder:false(插入顺序),true(访问顺序)
*/
super(16, DEFAULT_LOAD_FACTOR, true);
this.maxCapacity = maxCapacity;
} /**
* 是否需要删除最老的数据(即最近没有被访问的数据)
* @param eldest
* @return
*/
@Override
protected boolean removeEldestEntry(java.util.Map.Entry<K, V> eldest) {
return size() > maxCapacity;
} @Override
public V get(Object key) {
try {
lock.lock();
return super.get(key);
} finally {
lock.unlock();
}
} @Override
public V put(K key, V value) {
try {
lock.lock();
return super.put(key, value);
} finally {
lock.unlock();
}
} @Override
public V remove(Object key) {
try {
lock.lock();
return super.remove(key);
} finally {
lock.unlock();
}
} @Override
public int size() {
try {
lock.lock();
return super.size();
} finally {
lock.unlock();
}
}
...
}

注意:

  • 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=""只能配置在服务级别。

 <dubbo:reference id="demoService" check="false" interface="com.alibaba.dubbo.demo.DemoService" cache="threadlocal"/>
 public class ThreadLocalCacheFactory extends AbstractCacheFactory {
protected Cache createCache(URL url) {
return new ThreadLocalCache(url);
}
} public class ThreadLocalCache implements Cache {
private final ThreadLocal<Map<Object, Object>> store; public ThreadLocalCache(URL url) {
this.store = new ThreadLocal<Map<Object, Object>>() {
@Override
protected Map<Object, Object> initialValue() {
return new HashMap<Object, Object>();
}
};
} public void put(Object key, Object value) {
store.get().put(key, value);
} public Object get(Object key) {
return store.get().get(key);
}
}

ThreadLocalCache的实现是HashMap。

四、JCache缓存源码解析

//TODO

第十五章 dubbo结果缓存机制的更多相关文章

  1. Android笔记(二十五) ListView的缓存机制与BaseAdapter

    之前接触了ListView和Adapter,Adapter将数据源和View连接起来,实际应用中,我们要显示的数据往往有很多,而屏幕只有那么大,系统只能屏幕所能显示的内容,当我们滑动屏幕,会将旧的内容 ...

  2. Gradle 1.12用户指南翻译——第六十五章. Maven 发布(新)

    其他章节的翻译请参见:http://blog.csdn.net/column/details/gradle-translation.html翻译项目请关注Github上的地址:https://gith ...

  3. Gradle 1.12用户指南翻译——第四十五章. 应用程序插件

    本文由CSDN博客貌似掉线翻译,其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Githu ...

  4. Gradle 1.12用户指南翻译——第二十五章. Scala 插件

    其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Github上的地址: https://g ...

  5. Gradle 1.12用户指南翻译——第三十五章. Sonar 插件

    本文由CSDN博客万一博主翻译,其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Githu ...

  6. “全栈2019”Java多线程第二十四章:等待唤醒机制详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  7. 第十五章、Python多线程之信号量和GIL

    目录 第十五章.Python多线程之信号量和GIL 1. 信号量(Semaphore) 2. GIL 说明: 第十五章.Python多线程之信号量和GIL 1. 信号量(Semaphore) 信号量用 ...

  8. 20190901 On Java8 第十五章 异常

    第十五章 异常 要想创建健壮的系统,它的每一个构件都必须是健壮的. 异常概念 C++的异常处理机制基于 Ada,Java 中的异常处理则建立在 C++的基础之上(尽管看上去更像 Object Pasc ...

  9. 【C++】《C++ Primer 》第十五章

    第十五章 面向对象程序设计 一.OOP:概述 面向对象程序设计(OOP)的核心思想是数据抽象.继承和动态绑定. 通过使用数据抽象,可以将类的接口和实现分离. 使用继承,可以定义相似的类型并对其相似关系 ...

随机推荐

  1. myeclipse2017 安装包及破解插件的下载

    一:安装包下载 链接:http://pan.baidu.com/s/1miIFqha 密码:nz7h 二:操作步骤 因为工作中需要有多个MyEclipse去管理不同的项目组的工作,恰逢MyEclips ...

  2. 【linux】16进制格式查看命令hexdump

    test.txt内容 asdfsg ewtwfsdf1Hello World! hexdump -Cv test.txt   输出 |asdfsg ewtwfsdf1| 6c 6c 6f 6f 6c ...

  3. 使用@property - 廖雪峰的官方网站

    使用@property 阅读: 20616 在绑定属性时,如果我们直接把属性暴露出去,虽然写起来很简单,但是,没办法检查参数,导致可以把成绩随便改: s = Student() s.score = 9 ...

  4. Python 列表推导、迭代器与生成器

    1.列表推导 1 2 3 4 5 6 7 8 9 10 11 numbers = [i for i in range(10) if i % 2 == 0] print(numbers)   seq = ...

  5. 步步为营-20-XML

    说明:可扩展标记语言 eXtensible Markup Language--区分大小写 涉及到的知识点:DOM 文档对象模型 文本文件存储数据缺点:1,不易读取.2,易乱码 1 通过代码创建一个xm ...

  6. 2017-2018-2 20155309 南皓芯 Exp7 网络欺诈防范

    实践内容 本实践的目标理解常用网络欺诈背后的原理,以提高防范意识,并提出具体防范方法.具体实践有 1,简单应用SET工具建立冒名网站 2.ettercap DNS spoof 3.结合应用两种技术,用 ...

  7. [转] HTML5利用WebRTC的getUserMedia获取摄像头信息模拟拍照及视频(完整示例)

    <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <t ...

  8. canvas扩散圆环

    最近看了很多牛的动画,想想自己的canvas的确很菜. 公式在那里,但是不是太会套.找demo发现都是很难的 于是找了个简单的效果 圆环从中间扩散的效果 关键是 globalCompositeOper ...

  9. c++ primer 笔记 (三)

    标准库类型string 和 vector ,分别定义了大小可变的字符串和集合.                 bitset,提供了一个抽象方法来操作位的集合.提供更方便的处理位的方式(相对于整型值上 ...

  10. 文档工具GitBook使用

    一.登陆注册 地址:https://www.gitbook.com/ 1.gitbook可使用github账号登录,如果已经注册github可以直接使用github账号登录 2.如果是github账号 ...