You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dubbo.apache.org by il...@apache.org on 2018/06/14 02:14:58 UTC
[incubator-dubbo] branch master updated: add an ExpiryCache for
CacheFilter (#1707)
This is an automated email from the ASF dual-hosted git repository.
iluo pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-dubbo.git
The following commit(s) were added to refs/heads/master by this push:
new 928292b add an ExpiryCache for CacheFilter (#1707)
928292b is described below
commit 928292b1a1938dad96d3965e7f48da5dc0a09373
Author: 煦胤 <xi...@foxmail.com>
AuthorDate: Thu Jun 14 10:14:50 2018 +0800
add an ExpiryCache for CacheFilter (#1707)
* ExpiryCache - With the characteristic of expiration time
* ExpiryCache - With the characteristic of expiration time
* fix 1 Checkstyle violation
* add destroy method for cache to release resources
* add destroy method for cache to release resources
* fix a Checkstyle violation
* fix a Checkstyle violation
* add expiryCacheFactoryTest
* delete unused comments code
* fix ShutdownHook Close a non-SPI cache
* delete test code
* delete ExpiringMap's deamon thread release and rename Expiry to Expiring
* rename package expiry to expiring
* remove lock for ExpiryObject
* move unused import
* rollback my change made against CacheFilterTest
* add expiring into CacheFilterTest
---
.../com/alibaba/dubbo/common/utils/LRUCache.java | 12 +-
.../alibaba/dubbo/config/DubboShutdownHook.java | 8 +-
.../cache/support/expiring/ExpiringCache.java | 50 +++
.../support/expiring/ExpiringCacheFactory.java | 32 ++
.../dubbo/cache/support/expiring/ExpiringMap.java | 373 +++++++++++++++++++++
.../internal/com.alibaba.dubbo.cache.CacheFactory | 3 +-
.../dubbo/cache/filter/CacheFilterTest.java | 6 +-
.../support/expiring/ExpiringCacheFactoryTest.java | 38 +++
8 files changed, 512 insertions(+), 10 deletions(-)
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/LRUCache.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/LRUCache.java
index 9365d65..835772b 100644
--- a/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/LRUCache.java
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/LRUCache.java
@@ -46,8 +46,8 @@ public class LRUCache<K, V> extends LinkedHashMap<K, V> {
@Override
public boolean containsKey(Object key) {
+ lock.lock();
try {
- lock.lock();
return super.containsKey(key);
} finally {
lock.unlock();
@@ -56,8 +56,8 @@ public class LRUCache<K, V> extends LinkedHashMap<K, V> {
@Override
public V get(Object key) {
+ lock.lock();
try {
- lock.lock();
return super.get(key);
} finally {
lock.unlock();
@@ -66,8 +66,8 @@ public class LRUCache<K, V> extends LinkedHashMap<K, V> {
@Override
public V put(K key, V value) {
+ lock.lock();
try {
- lock.lock();
return super.put(key, value);
} finally {
lock.unlock();
@@ -76,8 +76,8 @@ public class LRUCache<K, V> extends LinkedHashMap<K, V> {
@Override
public V remove(Object key) {
+ lock.lock();
try {
- lock.lock();
return super.remove(key);
} finally {
lock.unlock();
@@ -86,8 +86,8 @@ public class LRUCache<K, V> extends LinkedHashMap<K, V> {
@Override
public int size() {
+ lock.lock();
try {
- lock.lock();
return super.size();
} finally {
lock.unlock();
@@ -96,8 +96,8 @@ public class LRUCache<K, V> extends LinkedHashMap<K, V> {
@Override
public void clear() {
+ lock.lock();
try {
- lock.lock();
super.clear();
} finally {
lock.unlock();
diff --git a/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/DubboShutdownHook.java b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/DubboShutdownHook.java
index 348c5e6..0da42da 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/DubboShutdownHook.java
+++ b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/DubboShutdownHook.java
@@ -68,6 +68,13 @@ public class DubboShutdownHook extends Thread {
// destroy all the registries
AbstractRegistryFactory.destroyAll();
// destroy all the protocols
+ destroyProtocols();
+ }
+
+ /**
+ * Destroy all the protocols.
+ */
+ private void destroyProtocols() {
ExtensionLoader<Protocol> loader = ExtensionLoader.getExtensionLoader(Protocol.class);
for (String protocolName : loader.getLoadedExtensions()) {
try {
@@ -81,5 +88,4 @@ public class DubboShutdownHook extends Thread {
}
}
-
}
diff --git a/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/expiring/ExpiringCache.java b/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/expiring/ExpiringCache.java
new file mode 100644
index 0000000..570d98a
--- /dev/null
+++ b/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/expiring/ExpiringCache.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.cache.support.expiring;
+
+import com.alibaba.dubbo.cache.Cache;
+import com.alibaba.dubbo.common.URL;
+
+import java.util.Map;
+
+/**
+ * ExpiringCache - With the characteristic of expiration time.
+ */
+public class ExpiringCache implements Cache {
+ private final Map<Object, Object> store;
+
+ public ExpiringCache(URL url) {
+ // cache time (second)
+ final int secondsToLive = url.getParameter("cache.seconds", 180);
+ // Cache check interval (second)
+ final int intervalSeconds = url.getParameter("cache.interval", 4);
+ ExpiringMap<Object, Object> expiringMap = new ExpiringMap<Object, Object>(secondsToLive, intervalSeconds);
+ expiringMap.getExpireThread().startExpiryIfNotStarted();
+ this.store = expiringMap;
+ }
+
+ @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/expiring/ExpiringCacheFactory.java b/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/expiring/ExpiringCacheFactory.java
new file mode 100644
index 0000000..e8e27f2
--- /dev/null
+++ b/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/expiring/ExpiringCacheFactory.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.cache.support.expiring;
+
+import com.alibaba.dubbo.cache.Cache;
+import com.alibaba.dubbo.cache.support.AbstractCacheFactory;
+import com.alibaba.dubbo.common.URL;
+
+/**
+ * ExpiringCacheFactory
+ */
+public class ExpiringCacheFactory extends AbstractCacheFactory {
+
+ @Override
+ protected Cache createCache(URL url) {
+ return new ExpiringCache(url);
+ }
+}
diff --git a/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/expiring/ExpiringMap.java b/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/expiring/ExpiringMap.java
new file mode 100644
index 0000000..8f05412
--- /dev/null
+++ b/dubbo-filter/dubbo-filter-cache/src/main/java/com/alibaba/dubbo/cache/support/expiring/ExpiringMap.java
@@ -0,0 +1,373 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.cache.support.expiring;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * can be expired map
+ * Contains a background thread that periodically checks if the data is out of date
+ */
+public class ExpiringMap<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 ExpiringMap() {
+ this(DEFAULT_TIME_TO_LIVE, DEFAULT_EXPIRATION_INTERVAL);
+ }
+
+ /**
+ * Constructor
+ *
+ * @param timeToLive time to live (second)
+ */
+ public ExpiringMap(int timeToLive) {
+ this(timeToLive, DEFAULT_EXPIRATION_INTERVAL);
+ }
+
+ public ExpiringMap(int timeToLive, int expirationInterval) {
+ this(new ConcurrentHashMap<K, ExpiryObject>(), timeToLive, expirationInterval);
+ }
+
+ private ExpiringMap(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 "ExpiringMap{" +
+ "delegateMap=" + delegateMap.toString() +
+ ", expireThread=" + expireThread.toString() +
+ '}';
+ }
+
+ /**
+ * can be expired object
+ */
+ private class ExpiryObject {
+ private K key;
+ private V value;
+ private AtomicLong lastAccessTime;
+
+ 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 = new AtomicLong(lastAccessTime);
+ }
+
+ public long getLastAccessTime() {
+ return lastAccessTime.get();
+ }
+
+ public void setLastAccessTime(long lastAccessTime) {
+ this.lastAccessTime.set(lastAccessTime);
+ }
+
+ 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 long timeToLiveMillis;
+ private long expirationIntervalMillis;
+ private volatile boolean running = false;
+ private final Thread expirerThread;
+
+ @Override
+ public String toString() {
+ return "ExpireThread{" +
+ ", 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 expiring Thread
+ */
+ public void startExpiring() {
+ if (!running) {
+ running = true;
+ expirerThread.start();
+ }
+ }
+
+ /**
+ * start thread
+ */
+ public void startExpiryIfNotStarted() {
+ if (running) {
+ return;
+ }
+ startExpiring();
+ }
+
+ /**
+ * stop thread
+ */
+ public void stopExpiring() {
+ if (running) {
+ running = false;
+ expirerThread.interrupt();
+ }
+ }
+
+ /**
+ * get thread state
+ *
+ * @return thread state
+ */
+ public boolean isRunning() {
+ return running;
+ }
+
+ /**
+ * get time to live
+ *
+ * @return time to live
+ */
+ public int getTimeToLive() {
+ return (int) timeToLiveMillis / 1000;
+ }
+
+ /**
+ * update time to live
+ *
+ * @param timeToLive time to live
+ */
+ public void setTimeToLive(long timeToLive) {
+ this.timeToLiveMillis = timeToLive * 1000;
+ }
+
+ /**
+ * get expiration interval
+ *
+ * @return expiration interval (second)
+ */
+ public int getExpirationInterval() {
+ return (int) expirationIntervalMillis / 1000;
+ }
+
+ /**
+ * set expiration interval
+ *
+ * @param expirationInterval expiration interval (second)
+ */
+ public void setExpirationInterval(long expirationInterval) {
+ this.expirationIntervalMillis = expirationInterval * 1000;
+ }
+ }
+}
+
+
+
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 6e3ddd0..65185d8 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
+expiring=com.alibaba.dubbo.cache.support.expiring.ExpiringCacheFactory
\ No newline at end of file
diff --git a/dubbo-filter/dubbo-filter-cache/src/test/java/com/alibaba/dubbo/cache/filter/CacheFilterTest.java b/dubbo-filter/dubbo-filter-cache/src/test/java/com/alibaba/dubbo/cache/filter/CacheFilterTest.java
index 760605c..582f022 100644
--- a/dubbo-filter/dubbo-filter-cache/src/test/java/com/alibaba/dubbo/cache/filter/CacheFilterTest.java
+++ b/dubbo-filter/dubbo-filter-cache/src/test/java/com/alibaba/dubbo/cache/filter/CacheFilterTest.java
@@ -17,6 +17,7 @@
package com.alibaba.dubbo.cache.filter;
import com.alibaba.dubbo.cache.CacheFactory;
+import com.alibaba.dubbo.cache.support.expiring.ExpiringCacheFactory;
import com.alibaba.dubbo.cache.support.jcache.JCacheFactory;
import com.alibaba.dubbo.cache.support.lru.LruCacheFactory;
import com.alibaba.dubbo.cache.support.threadlocal.ThreadLocalCacheFactory;
@@ -33,7 +34,7 @@ import org.junit.runners.Parameterized;
import java.util.Arrays;
import java.util.List;
-import static org.junit.runners.Parameterized.*;
+import static org.junit.runners.Parameterized.Parameters;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
@@ -59,7 +60,8 @@ public class CacheFilterTest {
return Arrays.asList(new Object[][]{
{"lru", new LruCacheFactory()},
{"jcache", new JCacheFactory()},
- {"threadlocal", new ThreadLocalCacheFactory()}
+ {"threadlocal", new ThreadLocalCacheFactory()},
+ {"expiring", new ExpiringCacheFactory()}
});
}
diff --git a/dubbo-filter/dubbo-filter-cache/src/test/java/com/alibaba/dubbo/cache/support/expiring/ExpiringCacheFactoryTest.java b/dubbo-filter/dubbo-filter-cache/src/test/java/com/alibaba/dubbo/cache/support/expiring/ExpiringCacheFactoryTest.java
new file mode 100644
index 0000000..cbb7bc5
--- /dev/null
+++ b/dubbo-filter/dubbo-filter-cache/src/test/java/com/alibaba/dubbo/cache/support/expiring/ExpiringCacheFactoryTest.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.cache.support.expiring;
+
+import com.alibaba.dubbo.cache.Cache;
+import com.alibaba.dubbo.cache.support.AbstractCacheFactory;
+import com.alibaba.dubbo.cache.support.AbstractCacheFactoryTest;
+import org.junit.Test;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+
+public class ExpiringCacheFactoryTest extends AbstractCacheFactoryTest {
+ @Test
+ public void testLruCacheFactory() throws Exception {
+ Cache cache = super.constructCache();
+ assertThat(cache instanceof ExpiringCache, is(true));
+ }
+
+ @Override
+ protected AbstractCacheFactory getCacheFactory() {
+ return new ExpiringCacheFactory();
+ }
+}
\ No newline at end of file
--
To stop receiving notification emails like this one, please contact
iluo@apache.org.