package com.jarvis.cache; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.jarvis.cache.annotation.Cache; import com.jarvis.cache.aop.CacheAopProxyChain; import com.jarvis.cache.exception.LoadDataTimeOutException; import com.jarvis.cache.lock.ILock; import com.jarvis.cache.to.AutoLoadConfig; import com.jarvis.cache.to.AutoLoadTO; import com.jarvis.cache.to.CacheKeyTO; import com.jarvis.cache.to.CacheWrapper; import com.jarvis.cache.to.ProcessingTO; /** * 数据加载器 * @author jiayu.qiu */ public class DataLoader { private static final Logger logger=LoggerFactory.getLogger(DataLoader.class); private AbstractCacheManager cacheManager; private CacheAopProxyChain pjp; private CacheKeyTO cacheKey; private Cache cache; private Object[] arguments; private AutoLoadTO autoLoadTO; private boolean isFirst=true; private long loadDataUseTime; private CacheWrapper<Object> cacheWrapper; private int tryCnt=0; public DataLoader() { } public DataLoader init(CacheAopProxyChain pjp, AutoLoadTO autoLoadTO, CacheKeyTO cacheKey, Cache cache, AbstractCacheManager cacheManager) { this.cacheManager=cacheManager; this.pjp=pjp; this.cacheKey=cacheKey; this.cache=cache; this.autoLoadTO=autoLoadTO; if(null == autoLoadTO) {// 用户请求 this.arguments=pjp.getArgs(); } else {// 来自AutoLoadHandler的请求 this.arguments=autoLoadTO.getArgs(); } this.isFirst=true; this.loadDataUseTime=0; this.tryCnt=0; return this; } public DataLoader init(CacheAopProxyChain pjp, CacheKeyTO cacheKey, Cache cache, AbstractCacheManager cacheManager, Object[] arguments) { this.cacheManager=cacheManager; this.pjp=pjp; this.cacheKey=cacheKey; this.cache=cache; this.autoLoadTO=null; this.arguments=arguments; this.isFirst=true; this.loadDataUseTime=0; this.tryCnt=0; return this; } public DataLoader init(CacheAopProxyChain pjp, Cache cache, AbstractCacheManager cacheManager) { return init(pjp, null, null, cache, cacheManager); } public DataLoader init(CacheAopProxyChain pjp, CacheKeyTO cacheKey, Cache cache, AbstractCacheManager cacheManager) { return init(pjp, null, cacheKey, cache, cacheManager); } public DataLoader loadData() throws Throwable { ProcessingTO processing=cacheManager.processing.get(cacheKey); ProcessingTO processingTO=null; if(null == processing) { processingTO=new ProcessingTO(); ProcessingTO _processing=cacheManager.processing.putIfAbsent(cacheKey, processingTO);// 为发减少数据层的并发,增加等待机制。 if(null != _processing) { processing=_processing;// 获取到第一个线程的ProcessingTO 的引用,保证所有请求都指向同一个引用 } } Object lock=null; String tname=Thread.currentThread().getName(); if(null == processing) {// 当前并发中的第一个请求 isFirst=true; lock=processingTO; logger.debug(tname + " first thread!"); try { doFirstRequest(processingTO); } catch(Throwable e) { processingTO.setError(e); throw e; } finally { processingTO.setFirstFinished(true); cacheManager.processing.remove(cacheKey); synchronized(lock) { lock.notifyAll(); } } } else { isFirst=false; lock=processing; doWaitRequest(processing, lock); } return this; } private void doFirstRequest(ProcessingTO processingTO) throws Throwable { ILock distributedLock=cacheManager.getLock(); if(null != distributedLock && cache.lockExpire() > 0) {// 开启分布式锁 String lockKey=cacheKey.getLockKey(); long startWait=processingTO.getStartTime(); do { if(distributedLock.tryLock(lockKey, cache.lockExpire())) {// 获得分布式锁 long start=System.currentTimeMillis(); try { getData(); } finally { if(System.currentTimeMillis() - start < cache.lockExpire() * 1000) {// 当锁没过期才需要释放锁,避免释放他人的锁 distributedLock.unlock(lockKey); } } break; } for(int i=0; i < 10; i++) {// 没有获得锁时,定时缓存尝试获取数据 cacheWrapper=cacheManager.get(cacheKey, pjp.getMethod(), this.arguments); if(null != cacheWrapper) { break; } Thread.sleep(20); } if(null != cacheWrapper) { break; } } while(System.currentTimeMillis() - startWait < cache.waitTimeOut()); if(null == cacheWrapper) { throw new LoadDataTimeOutException("load data for key \"" + cacheKey.getCacheKey() + "\" timeout(" + cache.waitTimeOut() + " seconds)."); } } else { getData(); } processingTO.setCache(cacheWrapper);// 本地缓存 } private void doWaitRequest(ProcessingTO processing, Object lock) throws Throwable { long startWait=processing.getStartTime(); String tname=Thread.currentThread().getName(); do {// 等待 if(null == processing) { break; } if(processing.isFirstFinished()) { CacheWrapper<Object> _cacheWrapper=processing.getCache();// 从本地缓存获取数据, 防止频繁去缓存服务器取数据,造成缓存服务器压力过大 logger.debug(tname + " do FirstFinished" + " is null :" + (null == _cacheWrapper)); if(null != _cacheWrapper) { cacheWrapper=_cacheWrapper; return; } Throwable error=processing.getError(); if(null != error) {// 当DAO出错时,直接抛异常 logger.debug(tname + " do error"); throw error; } break; } else { synchronized(lock) { logger.debug(tname + " do wait"); try { lock.wait(10);// 如果要测试lock对象是否有效,wait时间去掉就可以 } catch(InterruptedException ex) { logger.error(ex.getMessage(), ex); } } } } while(System.currentTimeMillis() - startWait < cache.waitTimeOut()); if(null == cacheWrapper) { cacheWrapper=cacheManager.get(cacheKey, pjp.getMethod(), this.arguments); } if(null == cacheWrapper) { if(tryCnt < cacheManager.getLoadDataTryCnt()) { tryCnt++; loadData(); } else { throw new LoadDataTimeOutException("cache for key \"" + cacheKey.getCacheKey() + "\" loaded " + tryCnt + " times."); } } } public boolean isFirst() { return isFirst; } public DataLoader getData() throws Throwable { try { if(null != autoLoadTO) { autoLoadTO.setLoading(true); } long loadDataStartTime=System.currentTimeMillis(); Object result=pjp.doProxyChain(arguments); loadDataUseTime=System.currentTimeMillis() - loadDataStartTime; AutoLoadConfig config=cacheManager.getAutoLoadHandler().getConfig(); if(config.isPrintSlowLog() && loadDataUseTime >= config.getSlowLoadTime()) { String className=pjp.getTargetClass().getName(); logger.error(className + "." + pjp.getMethod().getName() + ", use time:" + loadDataUseTime + "ms"); } buildCacheWrapper(result); } catch(Throwable e) { throw e; } finally { if(null != autoLoadTO) { autoLoadTO.setLoading(false); } } return this; } private void buildCacheWrapper(Object result) { int expire=cache.expire(); try { expire=cacheManager.getScriptParser().getRealExpire(cache.expire(), cache.expireExpression(), arguments, result); } catch(Exception e) { logger.error(e.getMessage(), e); } cacheWrapper=new CacheWrapper<Object>(result, expire); } public CacheWrapper<Object> getCacheWrapper() { if(null == cacheWrapper) { throw new RuntimeException("run loadData() or buildCacheWrapper() please!"); } return cacheWrapper; } public long getLoadDataUseTime() { return loadDataUseTime; } }