在高并发的场景里面经常会使用到localcache内容,但是一直没有一个很好的内存管理工具。在开发的时候发现了ehcache,这么一个开源的工具。唯一的缺点就是无法对于多块数据单元进行一个有效的管理,并且在数据过期的时候无法提供有效的更新机制,所以这里写了一个数据缓存池来满足这个需求。
下面是设计组织结构:
这里主要是在数据实体内部封装了数据更新器,这样在数据过期的时候可以调用更新器的方法。
1. Ehcache数据缓冲的具体代码:(主要是get方法内部进行数据更新,使用对象锁的方式来进行数据过期的并发控制,缺点是可能在非常高的并发里面会出现数据阻塞的现象,但是因为这里大部分都是内存的运算操作,所以相对来说阻塞的效果还好)
001 | package com.tmall.lafite.core.manager.localcache; |
003 | import java.util.List; |
005 | import net.sf.ehcache.Cache; |
006 | import net.sf.ehcache.CacheManager; |
007 | import net.sf.ehcache.Element; |
008 | import net.sf.ehcache.concurrent.LockType; |
009 | import net.sf.ehcache.concurrent.ReadWriteLockSync; |
010 | import net.sf.ehcache.config.CacheConfiguration; |
011 | import net.sf.ehcache.store.MemoryStoreEvictionPolicy; |
013 | import com.tmall.lafite.core.LafiteResult; |
014 | import com.tmall.lafite.core.ResultCode; |
021 | public class LafiteCache { |
023 | private CacheManager cacheManager = null ; |
024 | private Cache cacheImpl = null ; |
026 | ReadWriteLockSync rwLock = new ReadWriteLockSync(); |
028 | private int capability = 30 ; |
029 | private long expireTime = 30 ; |
031 | public static final int DEFAULT_CAPABILITY = 30 ; |
032 | public static final int DEFAULT_EXPIRETIME = 30 ; |
034 | private String cacheName = "Tair Local Cache" ; |
036 | public LafiteCache(String id, int capability, long expireTimeMS) { |
038 | this .capability = capability; |
039 | this .expireTime = expireTimeMS; |
042 | public void setExpireTime( long expireTimeMS) { |
043 | this .expireTime = expireTimeMS; |
046 | public void setCapacity( int cap) { |
047 | cacheImpl.getCacheConfiguration().setMaxEntriesLocalHeap(cap); |
050 | public long getExpireTime() { |
054 | @SuppressWarnings ( "deprecation" ) |
055 | public void initialize() { |
056 | CacheConfiguration cacheConfiguration = new CacheConfiguration(); |
057 | cacheConfiguration.setDiskPersistent( false ); |
059 | cacheConfiguration.name(cacheName) |
060 | .maxEntriesLocalHeap(capability) |
061 | .diskPersistent( false ) |
062 | .memoryStoreEvictionPolicy(MemoryStoreEvictionPolicy.LRU); |
067 | cacheManager = CacheManager.create(); |
069 | cacheImpl = new Cache(cacheConfiguration); |
070 | cacheManager.addCache(cacheImpl); |
076 | return ( int ) cacheImpl.getSize(); |
079 | public void destroy() { |
082 | cacheManager.shutdown(); |
085 | public void clear() { |
086 | rwLock.lock(LockType.WRITE); |
088 | cacheImpl.removeAll(); |
090 | rwLock.unlock(LockType.WRITE); |
094 | public void del(Object key) { |
095 | cacheImpl.remove(key); |
099 | public void put(Object key, Object value) { |
100 | cacheImpl.put( new Element(key, value)); |
104 | public LafiteResult get(Object key) { |
105 | LafiteResult lafiteResult = new LafiteResult(); |
107 | Element element = cacheImpl.get(key); |
108 | if (element == null ) { |
109 | lafiteResult.setError(ResultCode.Error.Cache.NO_DATA); |
112 | long now = System.currentTimeMillis(); |
114 | long pastTime = now - element.getLastUpdateTime(); |
115 | if (pastTime >= expireTime) { |
117 | synchronized (element) { |
118 | pastTime = now - element.getLastUpdateTime(); |
119 | if (pastTime >= expireTime) { |
121 | element.updateUpdateStatistics(); |
122 | lafiteResult.setError(ResultCode.Error.Cache.DATA_OVERDUE); |
127 | lafiteResult.setDefaultModel(element.getObjectValue()); |
131 | @SuppressWarnings ( "unchecked" ) |
132 | public List<Object> getKeys() { |
133 | List<Object> keys = cacheImpl.getKeys(); |
2. 数据逻辑单元定义(这里封装了数据容器单元,把原来的数据池方法传入,在数据更新的时候使用namespace来获取内存实体,进而来获取数据。)
(注:这里目前还没有想好是否把数据的初始化放在容器当中,这里暂时不放入。只是在容器里面进行初始化方法的调用,真正的数据设置方式由逻辑单元获取数据池进行自身的put)
01 | package com.tmall.lafite.core.manager.localcache.util; |
03 | import org.springframework.beans.factory.annotation.Autowired; |
05 | import com.tmall.lafite.core.LafiteResult; |
06 | import com.tmall.lafite.core.manager.localcache.LafiteContainer; |
13 | public abstract class LogicCenter { |
15 | private LafiteContainer lafiteContainer; |
21 | public abstract Object initialize(); |
29 | public abstract Object callBack(String namespace, Object key, LafiteResult lafiteResult); |
31 | public LafiteContainer getLafiteContainer() { |
32 | return lafiteContainer; |
3. 数据池(数据池,使用init来循环调用内存实体里的逻辑单元进行数据的初始化)
001 | package com.tmall.lafite.core.manager.localcache; |
004 | import java.util.ArrayList; |
005 | import java.util.List; |
006 | import java.util.Map; |
007 | import java.util.concurrent.ConcurrentHashMap; |
009 | import org.slf4j.Logger; |
010 | import org.slf4j.LoggerFactory; |
012 | import com.tmall.lafite.core.LafiteResult; |
013 | import com.tmall.lafite.core.ResultCode; |
014 | import com.tmall.lafite.core.manager.localcache.entity.CacheEntity; |
015 | import com.tmall.lafite.core.manager.localcache.util.LogicCenter; |
022 | public class LafiteContainer { |
024 | protected final Logger logger = LoggerFactory.getLogger(LafiteContainer. class ); |
026 | private Map<String, CacheEntity> cacheMap = new ConcurrentHashMap<String, CacheEntity>(); |
032 | * @param lafiteCache 缓存对象 |
033 | * @param logicCenter 逻辑对象 (包含:初始化方法和callback方法) |
060 | public LafiteResult get(String namespace, Object key) { |
061 | LafiteResult lafiteResult = new LafiteResult(); |
062 | CacheEntity cacheEntity = cacheMap.get(namespace); |
063 | if (cacheEntity == null ) { |
064 | lafiteResult.setError(ResultCode.Error.Cache.NO_CACHEENTITY); |
066 | LafiteCache lafiteCache = cacheEntity.getLafiteCache(); |
067 | LafiteResult result = lafiteCache.get(key); |
068 | if (ResultCode.Error.Cache.NO_DATA.equals(result.getError()) |
069 | || ResultCode.Error.Cache.DATA_OVERDUE.equals(result.getError())) { |
070 | cacheEntity.getLogicCenter().callBack(namespace, key, result); |
072 | lafiteResult = result; |
084 | public LafiteResult getAll(String namespace) { |
085 | LafiteResult lafiteResult = new LafiteResult(); |
086 | List<Object> objects = new ArrayList<Object>(); |
088 | CacheEntity cacheEntity = cacheMap.get(namespace); |
089 | if (cacheEntity == null ) { |
090 | lafiteResult.setError(ResultCode.Error.Cache.NO_CACHEENTITY); |
092 | LafiteCache lafiteCache = cacheEntity.getLafiteCache(); |
093 | List<Object> keys = lafiteCache.getKeys(); |
095 | lafiteResult.setError(ResultCode.Error.Cache.NO_DATA); |
097 | for (Object key : keys) { |
098 | LafiteResult lr = get(namespace, key); |
099 | objects.add(lr.getDefaultModel()); |
103 | lafiteResult.setDefaultModel(objects); |
114 | public LafiteResult put(String namespace, Object key, Object value) { |
115 | LafiteResult lafiteResult = new LafiteResult(); |
116 | CacheEntity cacheEntity = cacheMap.get(namespace); |
117 | if (cacheEntity == null ) { |
118 | lafiteResult.setError(ResultCode.Error.Cache.NO_CACHEENTITY); |
120 | LafiteCache lafiteCache = cacheEntity.getLafiteCache(); |
121 | lafiteCache.put(key, value); |
126 | public void setCacheMap(Map<String, CacheEntity> cacheMap) { |
127 | this .cacheMap = cacheMap; |
130 | public Map<String, CacheEntity> getCacheMap() { |
134 | public void initialize() { |
136 | new Thread( new Runnable() { |
139 | for (String key : cacheMap.keySet()) { |
141 | CacheEntity cacheEntity = cacheMap.get(key); |
142 | if (cacheEntity != null ) { |
143 | LogicCenter logicCenter = cacheEntity.getLogicCenter(); |
144 | if (logicCenter != null ) { |
145 | logicCenter.initialize(); |
148 | } catch (Exception e) { |
4. spring初始化方式
02 | <bean id= "permitRoleCache" class = "com.tmall.lafite.core.manager.localcache.LafiteCache" init-method= "initialize" > |
03 | <constructor-arg value= "_permit_role_cache_" /> |
04 | <constructor-arg value= "100" /> |
05 | <constructor-arg value= "2000" /> |
07 | <bean id= "permitRoleLogicCenter" class = "com.tmall.lafite.core.manager.permit.cache.PermitRoleLogicCenter" /> |
08 | <bean id= "permitRoleCacheEntity" class = "com.tmall.lafite.core.manager.localcache.entity.CacheEntity" > |
09 | <property name= "logicCenter" ref= "permitRoleLogicCenter" /> |
10 | <property name= "lafiteCache" ref= "permitRoleCache" /> |
15 | <bean id= "lafiteContainer" class = "com.tmall.lafite.core.manager.localcache.LafiteContainer" init-method= "initialize" > |
17 | <property name= "cacheMap" > |
19 | <entry key= "PermitCommon" value-ref= "permitCommonCacheEntity" /> |
20 | <entry key= "PermitAlgorithm" value-ref= "permitAlgorithmCacheEntity" /> |
21 | <entry key= "PermitRole" value-ref= "permitRoleCacheEntity" /> |
5. 使用示例
02 | private LafiteContainer lafiteContainer; |
04 | private String namespace = LafiteNameSpace.PermitCommon; |
06 | @SuppressWarnings ( "unchecked" ) |
08 | public List<PermitCommonDO> getPermitDOCache() { |
09 | LafiteResult lafiteResult = lafiteContainer.getAll(namespace); |
10 | if (lafiteResult.getDefaultModel() != null ) { |
11 | return (List<PermitCommonDO>) lafiteResult.getDefaultModel(); |