diff --git a/pom.xml b/pom.xml index d46ee1ac..534cc92b 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.jarvis autoload-cache - 0.5 + 0.6 jar AutoLoadCache http://maven.apache.org diff --git a/src/main/java/com/jarvis/cache/AutoLoadHandler.java b/src/main/java/com/jarvis/cache/AutoLoadHandler.java index 141ec6cb..b1ba371c 100644 --- a/src/main/java/com/jarvis/cache/AutoLoadHandler.java +++ b/src/main/java/com/jarvis/cache/AutoLoadHandler.java @@ -86,6 +86,17 @@ public AutoLoadTO getAutoLoadTO(String cacheKey) { return autoLoadMap.get(cacheKey); } + /** + * 重置自动加载时间 + * @param cacheKey + */ + public void resetAutoLoadLastLoadTime(String cacheKey) { + AutoLoadTO autoLoadTO=autoLoadMap.get(cacheKey); + if(null != autoLoadTO && !autoLoadTO.isLoading()) { + autoLoadTO.setLastLoadTime(1L); + } + } + public void shutdown() { running=false; autoLoadMap.clear(); @@ -104,14 +115,12 @@ public void setAutoLoadTO(AutoLoadTO autoLoadTO) { class SortRunnable implements Runnable { - private final AutoLoadTOComparator comparator=new AutoLoadTOComparator(); - @Override public void run() { while(running) { if(autoLoadMap.isEmpty() || autoLoadQueue.size() > 0) {// 如果没有数据 或 还有线程在处理,则继续等待 try { - Thread.sleep(500); + Thread.sleep(1000); } catch(InterruptedException e) { logger.error(e.getMessage(), e); } @@ -120,14 +129,8 @@ public void run() { AutoLoadTO tmpArr[]=new AutoLoadTO[autoLoadMap.size()]; tmpArr=autoLoadMap.values().toArray(tmpArr);// 复制引用 - if(null != config.getSortType()) { - switch(config.getSortType()) { - case OLDEST_FIRST: - Arrays.sort(tmpArr, comparator); - break; - default: - break; - } + if(null != config.getSortType() && null != config.getSortType().getComparator()) { + Arrays.sort(tmpArr, config.getSortType().getComparator()); } for(AutoLoadTO to: tmpArr) { try { @@ -161,11 +164,12 @@ private void loadCache(AutoLoadTO autoLoadTO) { if(null == autoLoadTO) { return; } + long now=System.currentTimeMillis(); if(autoLoadTO.getLastRequestTime() <= 0 || autoLoadTO.getLastLoadTime() <= 0) { return; } if(autoLoadTO.getRequestTimeout() > 0 - && (System.currentTimeMillis() - autoLoadTO.getLastRequestTime()) >= autoLoadTO.getRequestTimeout() * 1000) {// 如果超过一定时间没有请求数据,则从队列中删除 + && (now - autoLoadTO.getLastRequestTime()) >= autoLoadTO.getRequestTimeout() * 1000) {// 如果超过一定时间没有请求数据,则从队列中删除 autoLoadMap.remove(autoLoadTO.getCacheKey()); return; } @@ -173,18 +177,25 @@ private void loadCache(AutoLoadTO autoLoadTO) { autoLoadMap.remove(autoLoadTO.getCacheKey()); return; } + // 对于使用频率很低的数据,也可以考虑不用自动加载 + long difFirstRequestTime=now - autoLoadTO.getFirstRequestTime(); + long oneHourSecs=3600000L; + if(difFirstRequestTime > oneHourSecs && autoLoadTO.getAverageUseTime() < 1000 + && (autoLoadTO.getRequestTimes() / (difFirstRequestTime / oneHourSecs)) < 60) {// 使用率比较低的数据,没有必要使用自动加载。 + autoLoadMap.remove(autoLoadTO.getCacheKey()); + return; + } int diff; if(autoLoadTO.getExpire() >= 600) { diff=120; } else { diff=60; } - if(!autoLoadTO.isLoading() - && (System.currentTimeMillis() - autoLoadTO.getLastLoadTime()) >= (autoLoadTO.getExpire() - diff) * 1000) { + if(!autoLoadTO.isLoading() && (now - autoLoadTO.getLastLoadTime()) >= (autoLoadTO.getExpire() - diff) * 1000) { if(config.isCheckFromCacheBeforeLoad()) { CacheWrapper result=cacheGeterSeter.get(autoLoadTO.getCacheKey()); if(null != result && result.getLastLoadTime() > autoLoadTO.getLastLoadTime() - && (System.currentTimeMillis() - result.getLastLoadTime()) < (autoLoadTO.getExpire() - diff) * 1000) { + && (now - result.getLastLoadTime()) < (autoLoadTO.getExpire() - diff) * 1000) { autoLoadTO.setLastLoadTime(result.getLastLoadTime()); return; } diff --git a/src/main/java/com/jarvis/cache/CacheUtil.java b/src/main/java/com/jarvis/cache/CacheUtil.java index fd8565c1..5c780dd8 100644 --- a/src/main/java/com/jarvis/cache/CacheUtil.java +++ b/src/main/java/com/jarvis/cache/CacheUtil.java @@ -26,7 +26,7 @@ public class CacheUtil { private static final String SPLIT_STR="_"; - private static final Map processing=new ConcurrentHashMap(); + private static final Map processing=new ConcurrentHashMap(); private static final ReentrantLock lock=new ReentrantLock(); @@ -213,22 +213,22 @@ public static T proceed(ProceedingJoinPoint pjp, Cache cache, AutoLoadHandle return cacheWrapper.getCacheObject(); } - Long lastProcTime=null; + Boolean isProcessing=null; try { lock.lock(); - lastProcTime=processing.get(cacheKey);// 为发减少数据层的并发,增加等待机制。 - if(null == lastProcTime) { - processing.put(cacheKey, System.currentTimeMillis()); + isProcessing=processing.get(cacheKey);// 为发减少数据层的并发,增加等待机制。 + if(null == isProcessing) { + processing.put(cacheKey, Boolean.TRUE); } } finally { lock.unlock(); } AutoLoadConfig config=autoLoadHandler.getConfig(); - if(null == lastProcTime) { + if(null == isProcessing) { return loadData(pjp, autoLoadTO, cacheKey, cacheGeterSeter, expire, config); } long startWait=System.currentTimeMillis(); - while(System.currentTimeMillis() - startWait < 300) { + while(System.currentTimeMillis() - startWait < 500) { synchronized(lock) { try { lock.wait(); @@ -239,14 +239,11 @@ public static T proceed(ProceedingJoinPoint pjp, Cache cache, AutoLoadHandle if(null == processing.get(cacheKey)) {// 防止频繁去缓存取数据,造成缓存服务器压力过大 cacheWrapper=cacheGeterSeter.get(cacheKey); if(cacheWrapper != null) { - break; + return cacheWrapper.getCacheObject(); } } } - if(null == cacheWrapper) { - return loadData(pjp, autoLoadTO, cacheKey, cacheGeterSeter, expire, config); - } - return cacheWrapper.getCacheObject(); + return loadData(pjp, autoLoadTO, cacheKey, cacheGeterSeter, expire, config); } /** diff --git a/src/main/java/com/jarvis/cache/AutoLoadTOComparator.java b/src/main/java/com/jarvis/cache/comparator/AutoLoadOldestComparator.java similarity index 88% rename from src/main/java/com/jarvis/cache/AutoLoadTOComparator.java rename to src/main/java/com/jarvis/cache/comparator/AutoLoadOldestComparator.java index 2e26e51b..293e4235 100644 --- a/src/main/java/com/jarvis/cache/AutoLoadTOComparator.java +++ b/src/main/java/com/jarvis/cache/comparator/AutoLoadOldestComparator.java @@ -1,4 +1,4 @@ -package com.jarvis.cache; +package com.jarvis.cache.comparator; import java.util.Comparator; @@ -7,8 +7,9 @@ /** * 排序算法:越接近过期时间,越耗时的排在最前,即: System.currentTimeMillis() - autoLoadTO.getLastLoadTime()-autoLoadTO.getExpire()*1000 值越大,排在越前 * autoLoadTO.getAverageUseTime() 值越大,排在越前 + * @author jiayu.qiu */ -public class AutoLoadTOComparator implements Comparator { +public class AutoLoadOldestComparator implements Comparator { @Override public int compare(AutoLoadTO autoLoadTO1, AutoLoadTO autoLoadTO2) { diff --git a/src/main/java/com/jarvis/cache/comparator/AutoLoadRequestTimesComparator.java b/src/main/java/com/jarvis/cache/comparator/AutoLoadRequestTimesComparator.java new file mode 100644 index 00000000..9e2a0ed5 --- /dev/null +++ b/src/main/java/com/jarvis/cache/comparator/AutoLoadRequestTimesComparator.java @@ -0,0 +1,30 @@ +package com.jarvis.cache.comparator; + +import java.util.Comparator; + +import com.jarvis.cache.to.AutoLoadTO; + +/** + * 根据请求次数,倒序排序,请求次数越多,说明使用频率越高,造成并发的可能越大。 + * @author jiayu.qiu + */ +public class AutoLoadRequestTimesComparator implements Comparator { + + @Override + public int compare(AutoLoadTO autoLoadTO1, AutoLoadTO autoLoadTO2) { + if(autoLoadTO1 == null && autoLoadTO2 != null) { + return 1; + } else if(autoLoadTO1 != null && autoLoadTO2 == null) { + return -1; + } else if(autoLoadTO1 == null && autoLoadTO2 == null) { + return 0; + } + if(autoLoadTO1.getRequestTimes() > autoLoadTO2.getRequestTimes()) { + return -1; + } else if(autoLoadTO1.getRequestTimes() < autoLoadTO2.getRequestTimes()) { + return 1; + } + return 0; + } + +} diff --git a/src/main/java/com/jarvis/cache/memcache/CachePointCut.java b/src/main/java/com/jarvis/cache/memcache/CachePointCut.java index fadbb12f..517ff1e3 100644 --- a/src/main/java/com/jarvis/cache/memcache/CachePointCut.java +++ b/src/main/java/com/jarvis/cache/memcache/CachePointCut.java @@ -44,7 +44,7 @@ public void setCache(String cacheKey, CacheWrapper result, int exp public CacheWrapper get(String key) { return (CacheWrapper)memcachedClient.get(key); } - + /** * 批量删除缓存 * @param keys @@ -53,12 +53,13 @@ public void delete(List keys) { try { if(null != keys && !keys.isEmpty()) { for(String key: keys) { - memcachedClient.delete(key); + this.delete(key); } } } catch(Exception e) { } } + /** * 通过组成Key直接删除 * @param key @@ -66,24 +67,34 @@ public void delete(List keys) { public void delete(String key) { try { memcachedClient.delete(key); + autoLoadHandler.resetAutoLoadLastLoadTime(key); } catch(Exception e) { } } - - public void delete(@SuppressWarnings("rawtypes") Class cs, String method, Object[] arguments, - String subKeySpEL){ + + /** + * 根据默认缓存Key删除缓存 + * @param cs Class + * @param method + * @param arguments + * @param subKeySpEL + * @param deleteByPrefixKey 是否批量删除 + */ + public void deleteByDefaultCacheKey(@SuppressWarnings("rawtypes") Class cs, String method, Object[] arguments, String subKeySpEL) { String cacheKey=CacheUtil.getDefaultCacheKey(cs.getName(), method, arguments, subKeySpEL); this.delete(cacheKey); } + /** * 通过Spring EL 表达式,删除缓存 * @param keySpEL Spring EL表达式 * @param arguments 参数 */ - public void deleteBySpELKey(String keySpEL, Object[] arguments){ + public void deleteDefinedCacheKey(String keySpEL, Object[] arguments) { String cacheKey=CacheUtil.getDefinedCacheKey(keySpEL, arguments); this.delete(cacheKey); } + public AutoLoadHandler getAutoLoadHandler() { return autoLoadHandler; } diff --git a/src/main/java/com/jarvis/cache/redis/CachePointCut.java b/src/main/java/com/jarvis/cache/redis/CachePointCut.java index 3971f568..cebe749c 100644 --- a/src/main/java/com/jarvis/cache/redis/CachePointCut.java +++ b/src/main/java/com/jarvis/cache/redis/CachePointCut.java @@ -1,6 +1,5 @@ package com.jarvis.cache.redis; - import java.io.Serializable; import java.util.List; import java.util.Set; @@ -127,13 +126,13 @@ public void deleteByDefaultCacheKey(@SuppressWarnings("rawtypes") Class cs, Stri logger.error(ex.getMessage(), ex); } } - + /** * 通过Spring EL 表达式,删除缓存 * @param keySpEL Spring EL表达式 * @param arguments 参数 */ - public void deleteBySpELKey(String keySpEL, Object[] arguments){ + public void deleteDefinedCacheKey(String keySpEL, Object[] arguments) { String cacheKey=CacheUtil.getDefinedCacheKey(keySpEL, arguments); this.deleteByKey(cacheKey); } @@ -158,6 +157,13 @@ public Object doInRedis(RedisConnection connection) throws DataAccessException { byte[][] keys2=new byte[keys.size()][]; keys.toArray(keys2); connection.del(keys2); + + for(byte[] tmp: keys2) { + JdkSerializationRedisSerializer serializer= + (JdkSerializationRedisSerializer)redisTemplate.getValueSerializer(); + String tmpKey=(String)serializer.deserialize(tmp); + autoLoadHandler.resetAutoLoadLastLoadTime(tmpKey); + } } return null; } @@ -172,7 +178,7 @@ public Object doInRedis(RedisConnection connection) throws DataAccessException { byte[] key=redisTemplate.getStringSerializer().serialize(cacheKey); connection.del(key); - + autoLoadHandler.resetAutoLoadLastLoadTime(cacheKey); return null; } }); diff --git a/src/main/java/com/jarvis/cache/to/AutoLoadConfig.java b/src/main/java/com/jarvis/cache/to/AutoLoadConfig.java index 4ae13277..fac1e4d4 100644 --- a/src/main/java/com/jarvis/cache/to/AutoLoadConfig.java +++ b/src/main/java/com/jarvis/cache/to/AutoLoadConfig.java @@ -32,7 +32,7 @@ public class AutoLoadConfig { * 自动加载队列排序算法 */ private AutoLoadQueueSortType sortType=AutoLoadQueueSortType.NONE; - + /** * 加载数据之前去缓存服务器中检查,数据是否快过期,如果应用程序只是部署在一台服务器,设置为false, 如果部署到多台服务器,可以考虑设置为true */ @@ -84,12 +84,10 @@ public void setSlowLoadTime(int slowLoadTime) { this.slowLoadTime=slowLoadTime; } - public boolean isCheckFromCacheBeforeLoad() { return checkFromCacheBeforeLoad; } - public void setCheckFromCacheBeforeLoad(boolean checkFromCacheBeforeLoad) { this.checkFromCacheBeforeLoad=checkFromCacheBeforeLoad; } diff --git a/src/main/java/com/jarvis/cache/to/AutoLoadTO.java b/src/main/java/com/jarvis/cache/to/AutoLoadTO.java index 38dc1dd3..2e303577 100644 --- a/src/main/java/com/jarvis/cache/to/AutoLoadTO.java +++ b/src/main/java/com/jarvis/cache/to/AutoLoadTO.java @@ -16,12 +16,34 @@ public class AutoLoadTO implements Serializable { private Object args[]; + /** + * 缓存Key + */ private String cacheKey; - private long lastLoadTime=0; + /** + * 上次从DAO加载数据时间 + */ + private long lastLoadTime=0L; - private long lastRequestTime=0; + /** + * 上次请求数据时间 + */ + private long lastRequestTime=0L; + + /** + * 第一次请求数据时间 + */ + private long firstRequestTime=0L; + + /** + * 请求数据次数 + */ + private long requestTimes=0L; + /** + * 缓存过期时间 + */ private int expire; private long requestTimeout=7200L;// 缓存数据在 requestTimeout 秒之内没有使用了,就不进行自动加载数据 @@ -33,6 +55,9 @@ public class AutoLoadTO implements Serializable { */ private long loadCnt=0L; + /** + * 从DAO中加载数据,使用时间的总和 + */ private long useTotalTime=0L; public AutoLoadTO(String cacheKey, ProceedingJoinPoint joinPoint, Object args[], int expire, long requestTimeout) { @@ -52,7 +77,21 @@ public long getLastRequestTime() { } public void setLastRequestTime(long lastRequestTime) { - this.lastRequestTime=lastRequestTime; + synchronized(this) { + this.lastRequestTime=lastRequestTime; + if(firstRequestTime == 0) { + firstRequestTime=lastRequestTime; + } + requestTimes++; + } + } + + public long getFirstRequestTime() { + return firstRequestTime; + } + + public long getRequestTimes() { + return requestTimes; } public int getExpire() { @@ -103,9 +142,11 @@ public long getUseTotalTime() { * 记录用时 * @param useTime */ - public synchronized void addUseTotalTime(long useTime) { - this.loadCnt++; - this.useTotalTime+=useTotalTime; + public void addUseTotalTime(long useTime) { + synchronized(this) { + this.loadCnt++; + this.useTotalTime+=useTotalTime; + } } /** diff --git a/src/main/java/com/jarvis/cache/type/AutoLoadQueueSortType.java b/src/main/java/com/jarvis/cache/type/AutoLoadQueueSortType.java index bb2ff684..1690fb7e 100644 --- a/src/main/java/com/jarvis/cache/type/AutoLoadQueueSortType.java +++ b/src/main/java/com/jarvis/cache/type/AutoLoadQueueSortType.java @@ -1,19 +1,33 @@ package com.jarvis.cache.type; +import java.util.Comparator; + +import com.jarvis.cache.comparator.AutoLoadOldestComparator; +import com.jarvis.cache.comparator.AutoLoadRequestTimesComparator; +import com.jarvis.cache.to.AutoLoadTO; + public enum AutoLoadQueueSortType { /** * 默认顺序 */ - NONE(0), + NONE(0, null), /** * 越接近过期时间,越耗时的排在最前 */ - OLDEST_FIRST(1); + OLDEST_FIRST(1, new AutoLoadOldestComparator()), + + /** + * 根据请求次数,倒序排序,请求次数越多,说明使用频率越高,造成并发的可能越大。 + */ + REQUEST_TIMES_DESC(2, new AutoLoadRequestTimesComparator()); private Integer id; - private AutoLoadQueueSortType(Integer id) { + private Comparator comparator; + + private AutoLoadQueueSortType(Integer id, Comparator comparator) { this.id=id; + this.comparator=comparator; } public static AutoLoadQueueSortType getById(Integer id) { @@ -32,4 +46,9 @@ public static AutoLoadQueueSortType getById(Integer id) { public Integer getId() { return id; } + + public Comparator getComparator() { + return comparator; + } + }