本地缓存类

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