dubbo 缓存 - litter-fish/ReadSource GitHub Wiki
结果缓存,用于加速热门数据的访问速度, Dubbo提供声明式缓存,以减少用户加缓存的工作量
dubbo提供的三种缓存接口的入口
threadlocal=com.alibaba.dubbo.cache.support.threadlocal.ThreadLocalCacheFactory
lru=com.alibaba.dubbo.cache.support.lru.LruCacheFactory
jcache=com.alibaba.dubbo.cache.support.jcache.JCacheFactory
- lru 基于最近最少使用原则删除多余缓存,保持最热的数据被缓存
- threadlocal 当前线程缓存,比如一个页面渲染,用到很多 portal,每个 portal 都要去查用户信息,通过线程缓存,可以减少这种多余访问
- jcache 与 JSR107 集成,可以桥接各种缓存实现。
AbstractCacheFactory
public abstract class AbstractCacheFactory implements CacheFactory {
private final ConcurrentMap<String, Cache> caches = new ConcurrentHashMap<String, Cache>();
@Override
public Cache getCache(URL url, Invocation invocation) {
url = url.addParameter(Constants.METHOD_KEY, invocation.getMethodName());
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);
}
ThreadLocal
public class ThreadLocalCacheFactory extends AbstractCacheFactory {
@Override
protected Cache createCache(URL url) {
// 具体缓存层实现是ThreadLocalCache类型。此类型是基于线程本地变量
return new ThreadLocalCache(url);
}
}
public class ThreadLocalCache implements Cache {
//通过ThreadLocal把store 绑定到当前线程
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>();
}
};
}
@Override
public void put(Object key, Object value) {
store.get().put(key, value);
}
@Override
public Object get(Object key) {
return store.get().get(key);
}
}
LRU缓存淘汰算法
lru 基于最近最少使用原则删除多余缓存,保持最热的数据被缓存。 该类型的缓存是跨线程的
LRU原理 LRU(Least recently used,最近最少使用)
算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”。
- 新数据插入到链表头部
- 每当缓存命中(即缓存数据被访问),则将数据移到链表头部
- 当链表满的时候,将链表尾部的数据丢弃。
源码实现
public class LruCacheFactory extends AbstractCacheFactory {
@Override
protected Cache createCache(URL url) {
return new LruCache(url);
}
}
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() {
this(DEFAULT_MAX_CAPACITY);
}
public LRUCache(int maxCapacity) {
super(16, DEFAULT_LOAD_FACTOR, true);
this.maxCapacity = maxCapacity;
}
@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();
}
}
@Override
public void clear() {
try {
lock.lock();
super.clear();
} finally {
lock.unlock();
}
}
}
/**
* JCache
*/
public class JCache implements com.alibaba.dubbo.cache.Cache {
private final Cache<Object, Object> store;
public JCache(URL url) {
String method = url.getParameter(Constants.METHOD_KEY, "");
// 每个服务提供者每个方法,分配一个cache对象
String key = url.getAddress() + "." + url.getServiceKey() + "." + method;
// jcache parameter is the full-qualified class name of SPI implementation
// jcache 为SPI实现的全限定类名
String type = url.getParameter("jcache");
//通过CachingProvider 等jsr107规范相关接口 操作,这样,就能通过购spi 机制桥接各种缓存实现了
CachingProvider provider = type == null || type.length() == 0 ? Caching.getCachingProvider() : Caching.getCachingProvider(type);
CacheManager cacheManager = provider.getCacheManager();
Cache<Object, Object> cache = cacheManager.getCache(key);
if (cache == null) {
try {
//configure the cache
MutableConfiguration config =
new MutableConfiguration<Object, Object>()
.setTypes(Object.class, Object.class)
// 通过cache.write.expire 设置失效时间,默认为1分钟
.setExpiryPolicyFactory(CreatedExpiryPolicy.factoryOf(new Duration(TimeUnit.MILLISECONDS, url.getMethodParameter(method, "cache.write.expire", 60 * 1000))))
.setStoreByValue(false)
.setManagementEnabled(true)
.setStatisticsEnabled(true);
cache = cacheManager.createCache(key, config);
} catch (CacheException e) {
// concurrent cache initialization
cache = cacheManager.getCache(key);
}
}
this.store = cache;
}
@Override
public void put(Object key, Object value) {
store.put(key, value);
}
@Override
public Object get(Object key) {
return store.get(key);
}
}