第十五章 dubbo结果缓存机制
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结果缓存机制的更多相关文章
- Android笔记(二十五) ListView的缓存机制与BaseAdapter
之前接触了ListView和Adapter,Adapter将数据源和View连接起来,实际应用中,我们要显示的数据往往有很多,而屏幕只有那么大,系统只能屏幕所能显示的内容,当我们滑动屏幕,会将旧的内容 ...
- Gradle 1.12用户指南翻译——第六十五章. Maven 发布(新)
其他章节的翻译请参见:http://blog.csdn.net/column/details/gradle-translation.html翻译项目请关注Github上的地址:https://gith ...
- Gradle 1.12用户指南翻译——第四十五章. 应用程序插件
本文由CSDN博客貌似掉线翻译,其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Githu ...
- Gradle 1.12用户指南翻译——第二十五章. Scala 插件
其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Github上的地址: https://g ...
- Gradle 1.12用户指南翻译——第三十五章. Sonar 插件
本文由CSDN博客万一博主翻译,其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Githu ...
- “全栈2019”Java多线程第二十四章:等待唤醒机制详解
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...
- 第十五章、Python多线程之信号量和GIL
目录 第十五章.Python多线程之信号量和GIL 1. 信号量(Semaphore) 2. GIL 说明: 第十五章.Python多线程之信号量和GIL 1. 信号量(Semaphore) 信号量用 ...
- 20190901 On Java8 第十五章 异常
第十五章 异常 要想创建健壮的系统,它的每一个构件都必须是健壮的. 异常概念 C++的异常处理机制基于 Ada,Java 中的异常处理则建立在 C++的基础之上(尽管看上去更像 Object Pasc ...
- 【C++】《C++ Primer 》第十五章
第十五章 面向对象程序设计 一.OOP:概述 面向对象程序设计(OOP)的核心思想是数据抽象.继承和动态绑定. 通过使用数据抽象,可以将类的接口和实现分离. 使用继承,可以定义相似的类型并对其相似关系 ...
随机推荐
- OCM_第三天课程:Section1 —》表空间的操作和管理、服务配置
注:本文为原著(其内容来自 腾科教育培训课堂).阅读本文注意事项如下: 1:所有文章的转载请标注本文出处. 2:本文非本人不得用于商业用途.违者将承当相应法律责任. 3:该系列文章目录列表: 一:&l ...
- 为什么js中0.1+0.2不等于0.3,怎样处理使之相等?(转载)
为什么js中0.1+0.2不等于0.3,怎样处理使之相等? console.log(0.1+0.2===0.3)// true or false?? 在正常的数学逻辑思维中,0.1+0.2=0.3这个 ...
- python抓取bing主页背景图片
最初Python2写法: #!/usr/bin/env python # -*- coding:utf-8 -*- # -*- author:nancy -*- # python2抓取bing主页所有 ...
- linux压缩与解压
1.tar -zcvf /home/aaa.tar.gz /xahot tar -zcvf 打包后生成的文件名全路径 要打包的目录 解压 #tar -zxvf /usr/local/test.tar. ...
- ES6精简要点
没想到ES7都出来了(虽然并不大),想到自己ES6还没学完 现在就补补吧,记录下重点部分 Let的用法 用来声明块级变量 f1();function f1() { let n = 5; if (tru ...
- 【ioi2011】Dancing elephants
题解: 这题是lct并不难想 关键在于如何建图 如果把每个大象连向第一个不能处理的大象 那么cut操作要删除的就是一个点而不是边 所以可以采用先离散化, 之后对于存在的大象,用边连向第一个不能处理的大 ...
- BZOJ1036 [ZJOI2008]树的统计Count 树链剖分
欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ1036 题意概括 一个树,每个节点有一个权值.3种操作. 1:修改某一个节点的权值. 2:询问某两个 ...
- 026 使用大数据对网站基本指标PV案例的分析
案例: 使用电商网站的用户行为日志进行统计分析 一:准备 1.指标 PV:网页流浪量 UV:独立访客数 VV:访客的访问数,session次数 IP:独立的IP数 2.上传测试数据 3.查看第一条记录 ...
- 关于Jar包 和 war
Jar包: 别人写好的java类打包,将这些jar包引入你的项目中,然后就可以直接使用这些jar包中的类和属性以及方法,一般都会放在lib目录下 war 是web项目
- Unity 之 插件下载
Inventory Pro2.3.1: https://mega.nz/#!MIIx1bqI!_nTHyvT1IJsAxOtSEp_cSnc2OL9pqF3EBXS_N143_X8 NGUI 3.11 ...