基于EHcache实现高并发数据缓存池

在高并发的场景里面经常会使用到localcache内容,但是一直没有一个很好的内存管理工具。在开发的时候发现了ehcache,这么一个开源的工具。唯一的缺点就是无法对于多块数据单元进行一个有效的管理,并且在数据过期的时候无法提供有效的更新机制,所以这里写了一个数据缓存池来满足这个需求。

下面是设计组织结构:

153022_tuje_917881.jpg

这里主要是在数据实体内部封装了数据更新器,这样在数据过期的时候可以调用更新器的方法。

1. Ehcache数据缓冲的具体代码:(主要是get方法内部进行数据更新,使用对象锁的方式来进行数据过期的并发控制,缺点是可能在非常高的并发里面会出现数据阻塞的现象,但是因为这里大部分都是内存的运算操作,所以相对来说阻塞的效果还好)

001 package com.tmall.lafite.core.manager.localcache;
002  
003 import java.util.List;
004  
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;
012  
013 import com.tmall.lafite.core.LafiteResult;
014 import com.tmall.lafite.core.ResultCode;
015  
016 /**
017  * cache数据实体
018  * @author wangxiao
019  *
020  */
021 public class LafiteCache {
022      
023     private CacheManager cacheManager = null;
024     private Cache cacheImpl = null;
025      
026     ReadWriteLockSync rwLock = new ReadWriteLockSync();
027      
028     private int capability = 30;
029     private long expireTime = 30;
030      
031     public static final int DEFAULT_CAPABILITY = 30;
032     public static final int DEFAULT_EXPIRETIME = 30;
033      
034     private String cacheName = "Tair Local Cache";
035      
036     public LafiteCache(String id, int capability, long expireTimeMS) {
037         this.cacheName = id;
038         this.capability = capability;
039         this.expireTime = expireTimeMS;
040     }
041      
042     public void setExpireTime(long expireTimeMS) {
043         this.expireTime = expireTimeMS;
044     }
045      
046     public void setCapacity(int cap) {
047         cacheImpl.getCacheConfiguration().setMaxEntriesLocalHeap(cap);
048     }
049      
050     public long getExpireTime() {
051         return expireTime;
052     }
053  
054     @SuppressWarnings("deprecation")
055     public void initialize() {
056         CacheConfiguration cacheConfiguration = new CacheConfiguration();
057         cacheConfiguration.setDiskPersistent(false);
058          
059         cacheConfiguration.name(cacheName)
060                           .maxEntriesLocalHeap(capability)
061                           .diskPersistent(false)
062                           .memoryStoreEvictionPolicy(MemoryStoreEvictionPolicy.LRU);
063                          // .timeToLiveSeconds(expireTime);
064         //cacheConfiguration.transactionalMode("LOCAL");
065         //Configuration config = new Configuration().name(cacheName).cache(cacheConfiguration);
066          
067         cacheManager = CacheManager.create();
068          
069         cacheImpl =  new Cache(cacheConfiguration);
070         cacheManager.addCache(cacheImpl);
071         //cache = MemoryStore.create(cacheImpl, new UnboundedPool());
072          
073     }
074      
075     public int size() {
076         return (int) cacheImpl.getSize();
077     }
078      
079     public void destroy() {
080         cacheImpl.dispose();
081         //cacheManager.removeCache(cacheName);
082         cacheManager.shutdown();
083     }
084      
085     public void clear() {
086         rwLock.lock(LockType.WRITE);
087         try {
088             cacheImpl.removeAll();
089         finally {
090             rwLock.unlock(LockType.WRITE);
091         }
092     }
093      
094     public void del(Object key) {
095         cacheImpl.remove(key);
096         return;
097     }
098      
099     public voidput(Object key, Object value) {
100         cacheImpl.put(new Element(key, value));
101         return ;
102     }
103      
104     public LafiteResult get(Object key) {
105         LafiteResult lafiteResult = newLafiteResult();
106          
107         Element element = cacheImpl.get(key);
108         if (element == null) {
109             lafiteResult.setError(ResultCode.Error.Cache.NO_DATA);
110             return lafiteResult;
111         }
112         long now = System.currentTimeMillis();
113          
114         long pastTime = now - element.getLastUpdateTime();
115         if (pastTime >= expireTime) {
116             // double check
117             synchronized (element) {
118                 pastTime = now - element.getLastUpdateTime();
119                 if (pastTime >= expireTime) {
120                     // expired, update entry
121                     element.updateUpdateStatistics();
122                     lafiteResult.setError(ResultCode.Error.Cache.DATA_OVERDUE);
123                 }
124             }
125         }
126         // element object value never null;
127         lafiteResult.setDefaultModel(element.getObjectValue());
128         return lafiteResult;
129     }
130      
131     @SuppressWarnings("unchecked")
132     public List<Object> getKeys() {
133         List<Object> keys = cacheImpl.getKeys();
134         return keys;
135     }
136 }

2. 数据逻辑单元定义(这里封装了数据容器单元,把原来的数据池方法传入,在数据更新的时候使用namespace来获取内存实体,进而来获取数据。)

(注:这里目前还没有想好是否把数据的初始化放在容器当中,这里暂时不放入。只是在容器里面进行初始化方法的调用,真正的数据设置方式由逻辑单元获取数据池进行自身的put)

01 package com.tmall.lafite.core.manager.localcache.util;
02  
03 import org.springframework.beans.factory.annotation.Autowired;
04  
05 import com.tmall.lafite.core.LafiteResult;
06 import com.tmall.lafite.core.manager.localcache.LafiteContainer;
07  
08 /**
09  * 缓存逻辑单元
10  * @author wangxiao
11  *
12  */
13 public abstract class LogicCenter {
14     @Autowired
15     private LafiteContainer lafiteContainer;
16      
17     /**
18      * 初始化方法
19      * @return
20      */
21     public abstract Object initialize();
22      
23     /**
24      * 回调函数
25      * @param lafiteCache
26      * @param key
27      * @return
28      */
29     public abstractObject callBack(String namespace, Object key, LafiteResult lafiteResult);
30  
31     public LafiteContainer getLafiteContainer() {
32         return lafiteContainer;
33     }
34 }
3. 数据池(数据池,使用init来循环调用内存实体里的逻辑单元进行数据的初始化)
001 package com.tmall.lafite.core.manager.localcache;
002  
003  
004 import java.util.ArrayList;
005 import java.util.List;
006 import java.util.Map;
007 import java.util.concurrent.ConcurrentHashMap;
008  
009 import org.slf4j.Logger;
010 import org.slf4j.LoggerFactory;
011  
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;
016  
017 /**
018  * cache容器
019  * @author wangxiao
020  *
021  */
022 public class LafiteContainer {
023      
024     protected final Logger logger = LoggerFactory.getLogger(LafiteContainer.class);
025  
026     private Map<String, CacheEntity> cacheMap = newConcurrentHashMap<String, CacheEntity>();
027      
028     /**
029      * 注册缓存对象
030      * @param namespace
031      * @param key
032      * @param lafiteCache 缓存对象
033      * @param logicCenter 逻辑对象 (包含:初始化方法和callback方法)
034      * @return
035      */
036 //  public String register(String namespace, String key, LafiteCache lafiteCache, LogicCenter logicCenter) {
037 //      if(namespace != null && StringUtils.isEmpty(key) && lafiteCache != null) {
038 //          if(cacheMap.containsKey(namespace)) {
039 //              return ResultCode.Error.Cache.NAMESPACE_REPETITION;
040 //          }
041 //         
042 //          CacheEntity cacheEntity = new CacheEntity(lafiteCache, logicCenter);
043 //          try {
044 //              cacheEntity.initialize();
045 //          } catch (Exception e) {
046 //              logger.error("LafiteContainer.register ", e);
047 //          }
048 //          cacheMap.put(namespace, cacheEntity);
049 //          return null;
050 //      }
051 //      return ResultCode.Error.Cache.COMMON_PARAM_LOST;
052 //  }
053      
054     /**
055      * 获取cache内的数据
056      * @param namespace
057      * @param key
058      * @return
059      */
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);
065         else {
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);//数据过期触发callback事件
071             }
072             lafiteResult = result;
073         }
074          
075         return lafiteResult;
076     }
077      
078     /**
079      * 获取指定命名空间的全部数据
080      *  这里采用的是逐条遍历的方式
081      * @param namespace
082      * @return
083      */
084     public LafiteResult getAll(String namespace) {
085         LafiteResult lafiteResult = new LafiteResult();
086         List<Object> objects = new ArrayList<Object>();
087          
088         CacheEntity cacheEntity = cacheMap.get(namespace);
089         if(cacheEntity == null) {
090             lafiteResult.setError(ResultCode.Error.Cache.NO_CACHEENTITY);
091         else {
092             LafiteCache lafiteCache = cacheEntity.getLafiteCache();
093             List<Object> keys = lafiteCache.getKeys();
094             if(keys.isEmpty()) {
095                 lafiteResult.setError(ResultCode.Error.Cache.NO_DATA);
096             else {
097                 for(Object key : keys) {
098                     LafiteResult lr = get(namespace, key);
099                     objects.add(lr.getDefaultModel());
100                 }
101             }
102         }
103         lafiteResult.setDefaultModel(objects);
104         return lafiteResult;
105     }
106      
107     /**
108      * 设置数据对象内容
109      * @param namespace
110      * @param key
111      * @param value
112      * @return
113      */
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);
119         else {
120             LafiteCache lafiteCache = cacheEntity.getLafiteCache();
121             lafiteCache.put(key, value);
122         }
123         return lafiteResult;
124     }
125  
126     public void setCacheMap(Map<String, CacheEntity> cacheMap) {
127         this.cacheMap = cacheMap;
128     }
129  
130     public Map<String, CacheEntity> getCacheMap() {
131         return cacheMap;
132     }
133      
134     public void initialize() {
135          
136         new Thread(new Runnable() {
137             @Override
138             public void run() {
139                 for(String key : cacheMap.keySet()) {
140                     try {
141                         CacheEntity cacheEntity = cacheMap.get(key);
142                         if(cacheEntity != null) {
143                             LogicCenter logicCenter = cacheEntity.getLogicCenter();
144                             if(logicCenter != null) {
145                                 logicCenter.initialize();
146                             }
147                         }
148                     }catch (Exception e) {
149                         e.printStackTrace();
150                     }
151                 }
152                  
153             }
154         }).start();
155          
156     }
157 }
4. spring初始化方式
01 <!-- 权限角色缓存
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"/>
06  </bean>
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" />
11  </bean>
12  -->
13    
14  <!-- 缓存容器 -->
15  <bean id="lafiteContainer"class="com.tmall.lafite.core.manager.localcache.LafiteContainer"init-method="initialize">
16  <!-- 
17  <property name="cacheMap">
18       <map>
19           <entry key="PermitCommon" value-ref="permitCommonCacheEntity" />
20           <entry key="PermitAlgorithm" value-ref="permitAlgorithmCacheEntity"/>
21           <entry key="PermitRole" value-ref="permitRoleCacheEntity"/>
22       </map>
23     </property> 
24     -->
25  </bean>

5. 使用示例

01 @Autowired
02     private LafiteContainer lafiteContainer;
03      
04     private String namespace = LafiteNameSpace.PermitCommon;
05  
06     @SuppressWarnings("unchecked")
07     @Transactional
08     public List<PermitCommonDO> getPermitDOCache() {
09         LafiteResult lafiteResult = lafiteContainer.getAll(namespace);
10         if(lafiteResult.getDefaultModel() != null) {
11             return (List<PermitCommonDO>) lafiteResult.getDefaultModel();
12         }
13         return null;
14     }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值