dubbo緩存主要實現,對方法調用結果的緩存。
在服務消費方和提供方均可以配置使用緩存。
以消費方爲例,能夠配置全局緩存策略,這樣全部服務引用都啓動緩存
<dubbo:consumer cache="lru"/>
能夠對某個服務引用配置緩存策略
<dubbo:reference id="demoService" interface="demo.dubbo.api.DemoService" cache="lru" >
也支持對單個方法啓用緩存策略
<dubbo:reference id="demoService" interface="demo.dubbo.api.DemoService" >
<dubbo:method name="sayHello" cache="lru"> </dubbo:method>
</dubbo:reference>
服務方配置方法同樣。java
下面分析具體的實現過程設計模式
dubbo的緩存是經過過濾器實現的
經過 這篇博文 對註解Activate的認識,還有緩存的使用配置cache
這裏找到了對應的Filter實現CacheFilterapi
//Activate指明服務方和消費方均可以啓用緩存 @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))) { //invoker.getUrl().addParameter(Constants.METHOD_KEY, invocation.getMethodName()) //做爲緩存對象的key 可知不一樣的服務提供者,每一個方法都會單獨分配一個緩存對象 Cache cache = cacheFactory.getCache(invoker.getUrl().addParameter(Constants.METHOD_KEY, invocation.getMethodName())); if (cache != null) { //方法的參數做爲key String key = StringUtils.toArgumentString(invocation.getArguments()); if (cache != null && key != null) { Object value = cache.get(key); if (value != null) { //緩存命中,直接返回,也就是說, //這裏要注意,若是有多個過濾器,cache後面的過濾器不會執行 return new RpcResult(value); } Result result = invoker.invoke(invocation); if (!result.hasException()) { cache.put(key, result.getValue()); } return result; } } } return invoker.invoke(invocation); } }
關於cacheFactory的實現,這裏看下由dubbo動態生成的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; //默認是 lru 緩存策略 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])"); com.alibaba.dubbo.cache.CacheFactory extension = (com.alibaba.dubbo.cache.CacheFactory)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.cache.CacheFactory.class).getExtension(extName); return extension.getCache(arg0); } }
duobb提供了CacheFactory的具體有三種實現,類圖以下緩存
AbstractCacheFactory抽象父類中定義了緩存對象的獲取方法getCache數據結構
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; } //具體緩存實如今子類中,這也是dubbo一向的設計模式,公共方法提到抽象類中 protected abstract Cache createCache(URL url); }
//LruCacheFactory子類,返回LruCache對象,實現LRU策略緩存 public class LruCacheFactory extends AbstractCacheFactory { protected Cache createCache(URL url) { return new LruCache(url); } } //JCacheFactory子類,返回LruCache對象,可與符合JSR107規範的緩存橋接 public class JCacheFactory extends AbstractCacheFactory { protected Cache createCache(URL url) { return new JCache(url); } } //ThreadLocalCacheFactory子類,返回LruCache對象,ThreadLocal利用當前線程緩存 public class ThreadLocalCacheFactory extends AbstractCacheFactory { protected Cache createCache(URL url) { return new ThreadLocalCache(url); } }
上面提到的三種Cache對象類都實現了com.alibaba.dubbo.cache接口,類圖以下,併發
Cache接口很簡單ide
public interface Cache { //put value到緩存 void put(Object key, Object value); //經過key 獲取value Object get(Object key); }
三種Cache類都實現也值得學習
LruCache類函數
public class LruCache implements Cache { private final Map<Object, Object> store; public LruCache(URL url) { //從配置中,獲取cache大小 final int max = url.getParameter("cache.size", 1000); //內部是LRUCache對象 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); } }
分析LRUCache源碼,能夠看到LRUCache繼承了LinkedHashMap,而LinkedHashMap是個雙向鏈表,它是個自然的lru數據結構
只要定義LinkedHashMap是有序的,好比LRUCache的構造函數的定義學習
public LRUCache(int maxCapacity) { //默認有序的鏈表,初始數組申請空間大小16,負載因子0.75(觸發擴展的填充度臨界值) super(16, DEFAULT_LOAD_FACTOR, true); this.maxCapacity = maxCapacity; }
並重寫LinkedHashMap的removeEldestEntry方法
@Override //定義換出緩存對象的條,這裏是大小超過最大容量 protected boolean removeEldestEntry(java.util.Map.Entry<K, V> eldest) { return size() > maxCapacity; }
這樣就能夠完成一個LRU緩存容器的建立,具體實現,可讀寫LinkedHashMap源碼
ThreadLocalCache類實現
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>(); } }; } public void put(Object key, Object value) { store.get().put(key, value); } public Object get(Object key) { return store.get().get(key); } }
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 爲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分鐘,但不知道cache.write.expire在哪設置的?? .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) { // 初始化cache 的併發狀況 cache = cacheManager.getCache(key); } } this.store = cache; } public void put(Object key, Object value) { store.put(key, value); } public Object get(Object key) { return store.get(key); } }
個人博客即將搬運同步至騰訊雲+社區,邀請你們一同入駐:https://cloud.tencent.com/developer/support-plan?invite_code=15h2lq45eu17e