package com.pingan.haofang.agent.saas.util.cache.daemon; import java.util.HashMap; import java.util.concurrent.TimeUnit; import org.apache.log4j.Logger; /** * 增强型本地缓存 */ public class DaemonCache<K,V> { private static final Logger log = Logger.getLogger(DaemonCache.class); /** 对Null类型的值,用户希望设定的过期时间 ,时间单位为秒 */ private long nullValueExpiredTime = 10; /** 对于正常的缓存值,用户希望设定的过期时间,时间单位为秒 */ private long valueExpiredTime = 10; /** 对于通过getDataByKey接口获取数据是报错的情况,用户希望Null在本地缓存的过期时间,时间单位为秒*/ private long errorValueExpiredTime = 10; private static long LOCAL_CACHE_MAX_SIZE=100000l; /** 默认本地缓存大小 */ private long localCacheSize= LOCAL_CACHE_MAX_SIZE; /** 本地缓存核心,一个HashMap,最高效率读写*/ private final HashMap<K,LocalCacheValue<V>> localCache = new HashMap<K,LocalCacheValue<V>>(); /** DaemonCacheDataCallBack的实现,在创建一个DaemonCache,强制要求用户实现 */ private DaemonCacheDataCallBack<K,V> callback; /** 需要传入daemonCacheDataCallBack构造 */ public DaemonCache(DaemonCacheDataCallBack<K,V> daemonCacheDataCallBack){ if(daemonCacheDataCallBack==null){ throw new IllegalArgumentException("when create a new DaemonCache, please implmement the DaemonCacheDataCallBack first!"); } this.callback=daemonCacheDataCallBack; } /** * 创建DaemonCache实例 ,默认本地缓存大小为10万 * @param nullValueExpiredTime 空数据过期时间 * @param valueExpiredTime 正常时间期望过期时间 * @param errorValueExpiredTime 数据接口查询错误过期时间 */ public DaemonCache(long valueExpiredTime,long nullValueExpiredTime,long errorValueExpiredTime) { this.valueExpiredTime = valueExpiredTime; this.nullValueExpiredTime = nullValueExpiredTime; this.errorValueExpiredTime = errorValueExpiredTime; } /** * 创建DaemonCache实例 ,带有数据层逻辑回调实现,默认本地缓存大小为10万 * @param valueExpiredTime 空数据过期时间 ,时间单位为"秒" * @param nullValueExpiredTime 正常时间期望过期时间,时间单位为"秒" * @param errorValueExpiredTime 数据接口查询错误过期时间,时间单位为"秒" * @param callback 数据层逻辑回调实现,实现自DaemonCacheDataCallBack<K, V>接口 * 例子: * DaemonCache localCache = new DaemonCache<Integer, List<Item>>( 60,//nullValueExpiredTime 空数据过期时间 60 * 60,//valueExpiredTime 正常时间期望过期时间 60,//errorValueExpiredTime 数据接口查询错误过期时间 new DaemonCacheDataCallBack<Integer, List<Item>>() { @Override public List<Item> getDataByKey(Integer key) { return getTopItemsImpl(key); //这里为业务逻辑实现 } } ); */ public DaemonCache(long valueExpiredTime,long nullValueExpiredTime, long errorValueExpiredTime,DaemonCacheDataCallBack<K, V> callback) { if(callback==null){ throw new IllegalArgumentException("when create a new DaemonCache, please implmement the DaemonCacheDataCallBack first!"); } this.callback=callback; this.nullValueExpiredTime = nullValueExpiredTime; this.valueExpiredTime = valueExpiredTime; this.errorValueExpiredTime = errorValueExpiredTime; } /** * 获取默认的DaemonCache,过期时间默认为都为10s , 默认本地缓存大小为10万 * @param daemonCacheDataCallBack 数据层逻辑回调实现,实现自DaemonCacheDataCallBack<K, V>接口 * @return DaemonCache 默认的实例 */ public DaemonCache<K, V> getDefaultDaemonCache(DaemonCacheDataCallBack<K,V> daemonCacheDataCallBack){ if(daemonCacheDataCallBack==null){ throw new IllegalArgumentException("when create a new DaemonCache, please implmement the DaemonCacheDataCallBack first!"); } this.callback=daemonCacheDataCallBack; return new DaemonCache<K, V>(nullValueExpiredTime,valueExpiredTime,errorValueExpiredTime,daemonCacheDataCallBack); } /** * 通过key从Cache中获取对应的Value l * @param key <K> * @return V value */ public V get(K key){ if(localCache.size()>LOCAL_CACHE_MAX_SIZE){ log.debug("0-cache已经满了,直接返回业务数据"); //判断是否cache已经满了, 如果已近满了,那么直接返回结果 try { V callBackResult = callback.getDataByKey(key); return callBackResult; } catch (DaemonCacheDataCallBackException e) { e.printStackTrace(); return null; } } LocalCacheValue<V> localCacheValueResult = localCache.get(key); if (localCacheValueResult != null) { if (!localCacheValueResult.isExpired()) { // ->返回正常的业务数据,此时业务数据在本地缓存中不仅存在而且没有过期 //如果Value是NUll,并且没有过期,将直接返回Null log.debug("1-返回正常的业务数据,此时业务数据在本地缓存中不仅存在而且没有过期"); return localCacheValueResult.getValue(); } //->数据有,但是过期 log.debug("数据有,但是过期,调用数据查询接口(下层Memcache和DB的查询逻辑)"); try { //先调用数据查询接口(下层Memcache和DB的查询逻辑) V callBackResult = callback.getDataByKey(key); if(callBackResult==null){ //没有数据,向Cache中回写Null,并设定过期时间(这里的时间可能偏长) log.debug("2-没有数据,向Cache中回写Null,并设定过期时间(这里的时间可能偏长)"); putNULL(key, this.nullValueExpiredTime); return null; }else { //有数据,填充数据并设定时间(业务数据过期时间) log.debug("3-有数据,填充数据并设定时间(业务数据过期时间)"); put(key, new LocalCacheValue<V>(callBackResult), this.valueExpiredTime); return callBackResult; } } catch (DaemonCacheDataCallBackException e) { e.printStackTrace(); //数据查询报错,向Cache中回写Null,并设定过期时间(这里的时间可能偏短) log.debug("4-数据查询报错,向Cache中回写老数据,并设定过期时间(这里的时间可能偏短)"); put(key, localCacheValueResult, this.errorValueExpiredTime); return localCacheValueResult.getValue(); } } // -> 没有数据,或者为NULL(NULL数据已经过期) log.debug("没有数据,或者为NULL(NULL数据已经过期)"); try { // 先调用数据查询接口(下层Memcache和DB的查询逻辑) V callBackResult = callback.getDataByKey(key); if (callBackResult == null) { // 没有数据,向Cache中回写Null,并设定过期时间(这里的时间可能偏长) log.debug("5-无数据,向Cache中回写Null,并设定过期时间(这里的时间可能偏长)"); putNULL(key, this.nullValueExpiredTime); return null; } else { // 有数据,填充数据并设定时间(业务数据过期时间) log.debug("6-有数据,填充数据并设定时间(业务数据过期时间)"); put(key, new LocalCacheValue<V>(callBackResult),this.valueExpiredTime); return callBackResult; } } catch (DaemonCacheDataCallBackException e) { e.printStackTrace(); // 数据查询报错,向Cache中回写Null,并设定过期时间(这里的时间可能偏短) log.debug("7-数据查询报错,向Cache中回写Null,并设定过期时间(这里的时间可能偏短)"); putNULL(key, this.errorValueExpiredTime); return null; } } /** * 向localcache中放入Null值 * @param key cache的Key * @param expiredTime Null 值的过期时间 */ private void putNULL(K key, long expiredTime) { LocalCacheValue<V> nullValue = new LocalCacheValue<V>(); nullValue.setNullValue(true); put(key, nullValue , expiredTime); } /** * 向Cache中放入key和Value,需要指定过期时间 * @param key * @param value * @param expiredTime */ private void put(K key, LocalCacheValue<V> value , long expiredTime) { expiredTime = TimeUnit.NANOSECONDS.convert(expiredTime,TimeUnit.SECONDS ); value.setExpiredTime(expiredTime); value.setExpiredStartTime(System.nanoTime()); localCache.put(key, value); } /** * 注入对应的业务回调实例 * @param callback 数据层逻辑回调实现,实现自DaemonCacheDataCallBack<K, V>接口 */ public void setCallback(DaemonCacheDataCallBack<K, V> callback) { if (callback == null) { throw new IllegalArgumentException( "the callback is null here , please check !"); } this.callback = callback; } /** * 清空本地缓存 */ public void clearLocalCache(){ this.localCache.clear(); } /** * 设置本地cache大小,本地cache的最大值不得高于10万,默认本地缓存大小为10万 * @param localCacheSize */ public void setLocalCacheSize(long localCacheSize) { if (localCacheSize<=0||localCacheSize>LOCAL_CACHE_MAX_SIZE) { throw new IllegalArgumentException( "the local cache size must be less then the max :"+LOCAL_CACHE_MAX_SIZE); } this.localCacheSize = localCacheSize; } }
本地缓存类
猜你喜欢
转载自572327713.iteye.com/blog/2415574
今日推荐
周排行