You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@dubbo.apache.org by GitBox <gi...@apache.org> on 2018/04/26 14:23:48 UTC

[GitHub] ZhichX closed pull request #1706: ExpiryCache - With the characteristic of expiration time

ZhichX closed pull request #1706: ExpiryCache - With the characteristic of expiration time
URL: https://github.com/apache/incubator-dubbo/pull/1706
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/filter/CacheFilter.java b/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/filter/CacheFilter.java
index 219af4db18..5c3386109a 100644
--- a/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/filter/CacheFilter.java
+++ b/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/filter/CacheFilter.java
@@ -21,12 +21,7 @@
 import com.alibaba.dubbo.common.extension.Activate;
 import com.alibaba.dubbo.common.utils.ConfigUtils;
 import com.alibaba.dubbo.common.utils.StringUtils;
-import com.alibaba.dubbo.rpc.Filter;
-import com.alibaba.dubbo.rpc.Invocation;
-import com.alibaba.dubbo.rpc.Invoker;
-import com.alibaba.dubbo.rpc.Result;
-import com.alibaba.dubbo.rpc.RpcException;
-import com.alibaba.dubbo.rpc.RpcResult;
+import com.alibaba.dubbo.rpc.*;
 
 /**
  * CacheFilter
@@ -42,14 +37,15 @@ public void setCacheFactory(CacheFactory cacheFactory) {
         this.cacheFactory = cacheFactory;
     }
 
+    @Override
     public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
-        if (cacheFactory != null && ConfigUtils.isNotEmpty(invoker.getUrl().getMethodParameter(invocation.getMethodName(), Constants.CACHE_KEY))) {
+        if (null != cacheFactory && ConfigUtils.isNotEmpty(invoker.getUrl().getMethodParameter(invocation.getMethodName(), Constants.CACHE_KEY))) {
             Cache cache = cacheFactory.getCache(invoker.getUrl().addParameter(Constants.METHOD_KEY, invocation.getMethodName()));
-            if (cache != null) {
+            if (null != cache) {
                 String key = StringUtils.toArgumentString(invocation.getArguments());
-                if (cache != null && key != null) {
+                if (StringUtils.isNotEmpty(key)) {
                     Object value = cache.get(key);
-                    if (value != null) {
+                    if (null != value) {
                         return new RpcResult(value);
                     }
                     Result result = invoker.invoke(invocation);
diff --git a/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/AbstractCacheFactory.java b/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/AbstractCacheFactory.java
index 99855f1ac6..ad1d7e7829 100644
--- a/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/AbstractCacheFactory.java
+++ b/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/AbstractCacheFactory.java
@@ -31,10 +31,11 @@
 
     private final ConcurrentMap<String, Cache> caches = new ConcurrentHashMap<String, Cache>();
 
+    @Override
     public Cache getCache(URL url) {
         String key = url.toFullString();
         Cache cache = caches.get(key);
-        if (cache == null) {
+        if (null == cache) {
             caches.put(key, createCache(url));
             cache = caches.get(key);
         }
diff --git a/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/expiry/ExpiryCache.java b/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/expiry/ExpiryCache.java
new file mode 100644
index 0000000000..1ba37c059f
--- /dev/null
+++ b/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/expiry/ExpiryCache.java
@@ -0,0 +1,33 @@
+package com.alibaba.dubbo.cache.support.expiry;
+
+import com.alibaba.dubbo.cache.Cache;
+import com.alibaba.dubbo.common.URL;
+
+import java.util.Map;
+
+/**
+ * ExpiryCache - With the characteristic of expiration time .
+ */
+public class ExpiryCache implements Cache {
+    private final Map<Object, Object> store;
+
+    public ExpiryCache(URL url) {
+        // 缓存时间
+        final int secondsToLive = url.getParameter("cache.seconds", 180);
+        // 缓存检查的时间间隔
+        final int intervalSeconds = url.getParameter("cache.interval", 1);
+        ExpiryMap<Object, Object> expiryMap = new ExpiryMap<Object, Object>(secondsToLive, intervalSeconds);
+        expiryMap.getExpireThread().startExpiryIfNotStarted();
+        this.store = expiryMap;
+    }
+
+    @Override
+    public void put(Object key, Object value) {
+        store.put(key, value);
+    }
+
+    @Override
+    public Object get(Object key) {
+        return store.get(key);
+    }
+}
diff --git a/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/expiry/ExpiryCacheFactory.java b/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/expiry/ExpiryCacheFactory.java
new file mode 100644
index 0000000000..34287d7cee
--- /dev/null
+++ b/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/expiry/ExpiryCacheFactory.java
@@ -0,0 +1,16 @@
+package com.alibaba.dubbo.cache.support.expiry;
+
+import com.alibaba.dubbo.cache.Cache;
+import com.alibaba.dubbo.cache.support.AbstractCacheFactory;
+import com.alibaba.dubbo.common.URL;
+
+/**
+ * ExpiryCacheFactory
+ */
+public class ExpiryCacheFactory extends AbstractCacheFactory {
+    
+    @Override
+    protected Cache createCache(URL url) {
+        return new ExpiryCache(url);
+    }
+}
diff --git a/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/expiry/ExpiryMap.java b/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/expiry/ExpiryMap.java
new file mode 100644
index 0000000000..cfd7902348
--- /dev/null
+++ b/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/expiry/ExpiryMap.java
@@ -0,0 +1,413 @@
+package com.alibaba.dubbo.cache.support.expiry;
+
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+/**
+ * can be expired map
+ * Contains a background thread that periodically checks if the data is out of date
+ */
+public class ExpiryMap<K, V> implements Map<K, V> {
+
+    /**
+     * default time to live (second)
+     */
+    private static final int DEFAULT_TIME_TO_LIVE = 180;
+
+    /**
+     * default expire check interval (second)
+     */
+    private static final int DEFAULT_EXPIRATION_INTERVAL = 1;
+
+    private static volatile int expireCount = 1;
+
+    private final ConcurrentHashMap<K, ExpiryObject> delegateMap;
+
+    private final ExpireThread expireThread;
+
+    public ExpiryMap() {
+        this(DEFAULT_TIME_TO_LIVE, DEFAULT_EXPIRATION_INTERVAL);
+    }
+
+    /**
+     * Constructor
+     *
+     * @param timeToLive time to live (second)
+     */
+    public ExpiryMap(int timeToLive) {
+        this(timeToLive, DEFAULT_EXPIRATION_INTERVAL);
+    }
+
+    public ExpiryMap(int timeToLive, int expirationInterval) {
+        this(new ConcurrentHashMap<K, ExpiryObject>(), timeToLive, expirationInterval);
+    }
+
+    private ExpiryMap(ConcurrentHashMap<K, ExpiryObject> delegateMap, int timeToLive, int expirationInterval) {
+        this.delegateMap = delegateMap;
+        this.expireThread = new ExpireThread();
+        expireThread.setTimeToLive(timeToLive);
+        expireThread.setExpirationInterval(expirationInterval);
+    }
+
+    @Override
+    public V put(K key, V value) {
+        ExpiryObject answer = delegateMap.put(key, new ExpiryObject(key, value, System.currentTimeMillis()));
+        if (answer == null) {
+            return null;
+        }
+        return answer.getValue();
+    }
+
+    @Override
+    public V get(Object key) {
+        ExpiryObject object = delegateMap.get(key);
+        if (object != null) {
+            object.setLastAccessTime(System.currentTimeMillis());
+            return object.getValue();
+        }
+        return null;
+    }
+
+    @Override
+    public V remove(Object key) {
+        ExpiryObject answer = delegateMap.remove(key);
+        if (answer == null) {
+            return null;
+        }
+        return answer.getValue();
+    }
+
+    @Override
+    public boolean containsKey(Object key) {
+        return delegateMap.containsKey(key);
+    }
+
+    @Override
+    public boolean containsValue(Object value) {
+        return delegateMap.containsValue(value);
+    }
+
+    @Override
+    public int size() {
+        return delegateMap.size();
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return delegateMap.isEmpty();
+    }
+
+    @Override
+    public void clear() {
+        delegateMap.clear();
+        expireThread.stopExpiring();
+    }
+
+    @Override
+    public int hashCode() {
+        return delegateMap.hashCode();
+    }
+
+    @Override
+    public Set<K> keySet() {
+        return delegateMap.keySet();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        return delegateMap.equals(obj);
+    }
+
+    @Override
+    public void putAll(Map<? extends K, ? extends V> inMap) {
+        for (Entry<? extends K, ? extends V> e : inMap.entrySet()) {
+            this.put(e.getKey(), e.getValue());
+        }
+    }
+
+    @Override
+    public Collection<V> values() {
+        List<V> list = new ArrayList<V>();
+        Set<Entry<K, ExpiryObject>> delegatedSet = delegateMap.entrySet();
+        for (Entry<K, ExpiryObject> entry : delegatedSet) {
+            ExpiryObject value = entry.getValue();
+            list.add(value.getValue());
+        }
+        return list;
+    }
+
+    @Override
+    public Set<Entry<K, V>> entrySet() {
+        throw new UnsupportedOperationException();
+    }
+
+    public ExpireThread getExpireThread() {
+        return expireThread;
+    }
+
+    public int getExpirationInterval() {
+        return expireThread.getExpirationInterval();
+    }
+
+    public void setExpirationInterval(int expirationInterval) {
+        expireThread.setExpirationInterval(expirationInterval);
+    }
+
+    public int getTimeToLive() {
+        return expireThread.getTimeToLive();
+    }
+
+    public void setTimeToLive(int timeToLive) {
+        expireThread.setTimeToLive(timeToLive);
+    }
+
+    @Override
+    public String toString() {
+        return "ExpiryMap{" +
+                "delegateMap=" + delegateMap.toString() +
+                ", expireThread=" + expireThread.toString() +
+                '}';
+    }
+
+    /**
+     * can be expired object
+     */
+    private class ExpiryObject {
+        private K key;
+        private V value;
+        private long lastAccessTime;
+        private final ReadWriteLock lastAccessTimeLock = new ReentrantReadWriteLock();
+
+        ExpiryObject(K key, V value, long lastAccessTime) {
+            if (value == null) {
+                throw new IllegalArgumentException("An expiring object cannot be null.");
+            }
+            this.key = key;
+            this.value = value;
+            this.lastAccessTime = lastAccessTime;
+        }
+
+        public long getLastAccessTime() {
+            lastAccessTimeLock.readLock().lock();
+            try {
+                return lastAccessTime;
+            } finally {
+                lastAccessTimeLock.readLock().unlock();
+            }
+        }
+
+        public void setLastAccessTime(long lastAccessTime) {
+            lastAccessTimeLock.writeLock().lock();
+            try {
+                this.lastAccessTime = lastAccessTime;
+            } finally {
+                lastAccessTimeLock.writeLock().unlock();
+            }
+        }
+
+        public K getKey() {
+            return key;
+        }
+
+        public V getValue() {
+            return value;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            return value.equals(obj);
+        }
+
+        @Override
+        public int hashCode() {
+            return value.hashCode();
+        }
+
+        @Override
+        public String toString() {
+            return "ExpiryObject{" +
+                    "key=" + key +
+                    ", value=" + value +
+                    ", lastAccessTime=" + lastAccessTime +
+                    '}';
+        }
+    }
+
+    /**
+     * Background thread, periodically checking if the data is out of date
+     */
+    public class ExpireThread implements Runnable {
+        private final ReadWriteLock stateLock = new ReentrantReadWriteLock();
+        private long timeToLiveMillis;
+        private long expirationIntervalMillis;
+        private volatile boolean running = false;
+        private final Thread expirerThread;
+
+        @Override
+        public String toString() {
+            return "ExpireThread{" +
+                    "stateLock=" + stateLock +
+                    ", timeToLiveMillis=" + timeToLiveMillis +
+                    ", expirationIntervalMillis=" + expirationIntervalMillis +
+                    ", running=" + running +
+                    ", expirerThread=" + expirerThread +
+                    '}';
+        }
+
+        public ExpireThread() {
+            expirerThread = new Thread(this, "ExpiryMapExpire-" + expireCount++);
+            expirerThread.setDaemon(true);
+        }
+
+        @Override
+        public void run() {
+            while (running) {
+                processExpires();
+                try {
+                    Thread.sleep(expirationIntervalMillis);
+                } catch (InterruptedException e) {
+                    running = false;
+                }
+            }
+        }
+
+        private void processExpires() {
+            long timeNow = System.currentTimeMillis();
+            for (ExpiryObject o : delegateMap.values()) {
+                if (timeToLiveMillis <= 0) {
+                    continue;
+                }
+                long timeIdle = timeNow - o.getLastAccessTime();
+                if (timeIdle >= timeToLiveMillis) {
+                    delegateMap.remove(o.getKey());
+                }
+            }
+        }
+
+        /**
+         * start expiry Thread
+         */
+        public void startExpiring() {
+            stateLock.writeLock().lock();
+            try {
+                if (!running) {
+                    running = true;
+                    expirerThread.start();
+                    Runtime.getRuntime().addShutdownHook(new Thread() {
+                        @Override
+                        public void run() {
+                            stopExpiring();
+                        }
+                    });
+                }
+            } finally {
+                stateLock.writeLock().unlock();
+            }
+        }
+
+        /**
+         * start thread
+         */
+        public void startExpiryIfNotStarted() {
+            stateLock.readLock().lock();
+            try {
+                if (running) {
+                    return;
+                }
+            } finally {
+                stateLock.readLock().unlock();
+            }
+            startExpiring();
+        }
+
+        /**
+         * stop thread
+         */
+        public void stopExpiring() {
+            stateLock.writeLock().lock();
+            try {
+                if (running) {
+                    running = false;
+                    expirerThread.interrupt();
+                }
+            } finally {
+                stateLock.writeLock().unlock();
+            }
+        }
+
+        /**
+         * get thread state
+         *
+         * @return thread state
+         */
+        public boolean isRunning() {
+            stateLock.readLock().lock();
+            try {
+                return running;
+            } finally {
+                stateLock.readLock().unlock();
+            }
+        }
+
+        /**
+         * get time to live
+         *
+         * @return time to live
+         */
+        public int getTimeToLive() {
+            stateLock.readLock().lock();
+            try {
+                return (int) timeToLiveMillis / 1000;
+            } finally {
+                stateLock.readLock().unlock();
+            }
+        }
+
+        /**
+         * update time to live
+         *
+         * @param timeToLive time to live
+         */
+        public void setTimeToLive(long timeToLive) {
+            stateLock.writeLock().lock();
+            try {
+                this.timeToLiveMillis = timeToLive * 1000;
+            } finally {
+                stateLock.writeLock().unlock();
+            }
+        }
+
+        /**
+         * get expiration interval
+         *
+         * @return expiration interval (second)
+         */
+        public int getExpirationInterval() {
+            stateLock.readLock().lock();
+            try {
+                return (int) expirationIntervalMillis / 1000;
+            } finally {
+                stateLock.readLock().unlock();
+            }
+        }
+
+        /**
+         * set expiration interval
+         *
+         * @param expirationInterval expiration interval (second)
+         */
+        public void setExpirationInterval(long expirationInterval) {
+            stateLock.writeLock().lock();
+            try {
+                this.expirationIntervalMillis = expirationInterval * 1000;
+            } finally {
+                stateLock.writeLock().unlock();
+            }
+        }
+    }
+}
+
+
+
diff --git a/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/jcache/JCache.java b/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/jcache/JCache.java
index 142e878a03..1b3ccddf66 100644
--- a/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/jcache/JCache.java
+++ b/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/jcache/JCache.java
@@ -66,10 +66,12 @@ public JCache(URL url) {
         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);
     }
diff --git a/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/jcache/JCacheFactory.java b/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/jcache/JCacheFactory.java
index 88b997481a..edc317ffc6 100644
--- a/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/jcache/JCacheFactory.java
+++ b/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/jcache/JCacheFactory.java
@@ -26,6 +26,7 @@
  */
 public class JCacheFactory extends AbstractCacheFactory {
 
+    @Override
     protected Cache createCache(URL url) {
         return new JCache(url);
     }
diff --git a/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/lru/LruCache.java b/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/lru/LruCache.java
index e84b0bee96..0611d69888 100644
--- a/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/lru/LruCache.java
+++ b/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/lru/LruCache.java
@@ -35,10 +35,12 @@ public LruCache(URL url) {
         this.store = new LRUCache<Object, Object>(max);
     }
 
+    @Override
     public void put(Object key, Object value) {
         store.put(key, value);
     }
 
+    @Override
     public Object get(Object key) {
         return store.get(key);
     }
diff --git a/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/lru/LruCacheFactory.java b/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/lru/LruCacheFactory.java
index f2cc3687e0..2d41f8e77c 100644
--- a/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/lru/LruCacheFactory.java
+++ b/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/lru/LruCacheFactory.java
@@ -26,6 +26,7 @@
  */
 public class LruCacheFactory extends AbstractCacheFactory {
 
+    @Override
     protected Cache createCache(URL url) {
         return new LruCache(url);
     }
diff --git a/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/threadlocal/ThreadLocalCache.java b/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/threadlocal/ThreadLocalCache.java
index 05411f82a8..c154674d44 100644
--- a/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/threadlocal/ThreadLocalCache.java
+++ b/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/threadlocal/ThreadLocalCache.java
@@ -30,6 +30,7 @@
 
     private final ThreadLocal<Map<Object, Object>> store;
 
+    // TODO This url is not used
     public ThreadLocalCache(URL url) {
         this.store = new ThreadLocal<Map<Object, Object>>() {
             @Override
@@ -39,10 +40,12 @@ public ThreadLocalCache(URL url) {
         };
     }
 
+    @Override
     public void put(Object key, Object value) {
         store.get().put(key, value);
     }
 
+    @Override
     public Object get(Object key) {
         return store.get().get(key);
     }
diff --git a/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/threadlocal/ThreadLocalCacheFactory.java b/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/threadlocal/ThreadLocalCacheFactory.java
index f28582d3ad..1f3c6102f6 100644
--- a/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/threadlocal/ThreadLocalCacheFactory.java
+++ b/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/threadlocal/ThreadLocalCacheFactory.java
@@ -26,6 +26,7 @@
  */
 public class ThreadLocalCacheFactory extends AbstractCacheFactory {
 
+    @Override
     protected Cache createCache(URL url) {
         return new ThreadLocalCache(url);
     }
diff --git a/dubbo-filter/dubbo-filter-cache/src/main/resources/META-INF/dubbo/internal/com.alibaba.dubbo.cache.CacheFactory b/dubbo-filter/dubbo-filter-cache/src/main/resources/META-INF/dubbo/internal/com.alibaba.dubbo.cache.CacheFactory
index 6e3ddd0e04..0aa8e42628 100644
--- a/dubbo-filter/dubbo-filter-cache/src/main/resources/META-INF/dubbo/internal/com.alibaba.dubbo.cache.CacheFactory
+++ b/dubbo-filter/dubbo-filter-cache/src/main/resources/META-INF/dubbo/internal/com.alibaba.dubbo.cache.CacheFactory
@@ -1,3 +1,4 @@
 threadlocal=com.alibaba.dubbo.cache.support.threadlocal.ThreadLocalCacheFactory
 lru=com.alibaba.dubbo.cache.support.lru.LruCacheFactory
-jcache=com.alibaba.dubbo.cache.support.jcache.JCacheFactory
\ No newline at end of file
+jcache=com.alibaba.dubbo.cache.support.jcache.JCacheFactory
+expiry=com.alibaba.dubbo.cache.support.expiry.ExpiryCacheFactory
\ No newline at end of file


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
users@infra.apache.org


With regards,
Apache Git Services

---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org
For additional commands, e-mail: notifications-help@dubbo.apache.org