设为首页 收藏本站
查看: 977|回复: 0

[经验分享] memcached整合ibatis

[复制链接]

尚未签到

发表于 2015-9-1 07:31:50 | 显示全部楼层 |阅读模式
ibatis自带的本地缓存有FIFO,LRU等,对于分布式缓存也有osCache支持,而最常用的memcached也可以整合到ibatis里滴,这样通过map关系配置,就省了很多硬编码。
首先写个实现CacheController接口的MemcachedIbatisController类
/**
* ibatis管理memcache 使用LRU算法
* @author langke93
* @date 2011-01-17
* @usage:
*  <cacheModel id="cache-videoinfo" type="com.woyo.upload.kernel.util.MemcachedIbatisController">
      <flushInterval seconds="3600"/>
       <flushOnExecute statement="updateVideoInfo" />
     <flushOnExecute statement="insertVideoInfo" />
     <flushOnExecute statement="deleteVideoInfo" />
      <property name="size" value="1000" />
    </cacheModel>
    @modify:
     cacheSize默认为0 ,表示由memcache管理缓存大小,推荐在集群中使用
*/
package com.woyo.upload.kernel.util;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
import org.apache.log4j.Logger;
import com.ibatis.sqlmap.engine.cache.CacheController;
import com.ibatis.sqlmap.engine.cache.CacheModel;
public class MemcachedIbatisController implements CacheController {
    protected final Logger log = Logger.getLogger(this.getClass());
    private MemCachedManager cache;   
    private List<String> keyList;//keyList 管理key
    private int cacheSize;   
  
    public MemcachedIbatisController(){
        this.cacheSize = 0; //默认为0表示不限制缓存大小,不使用LRU
        this.cache = MemCachedManager.getInstance();
        this.keyList = getKeyListInstance();
        log.info("       Enable MemcachedIbatisController !");
    }   
    public synchronized List<String> getKeyListInstance(){
     if(keyList == null){
      keyList =  Collections.synchronizedList(new LinkedList<String>());
     }
     return keyList;
    }
    public void flush(CacheModel cacheModel) {
     String key = null;   
        try {
         if(keyList.isEmpty()){
          //cache.flush(); 目前清除缓存仅依赖于缓存过期,分表/条件清除缓存还需要做扩展
         }else
            for (int i=0;i<keyList.size();i++) {
             key = keyList.get(i);
          cache.delete(key);
          keyList.remove(key);
            }
        } catch (Exception e) {   
            e.printStackTrace();   
        }
        //keyList.clear();
        log.info("       flush memcache!");
    }
    /**
     * 实现LRU算法
     * 把最近取过的数据放到栈顶
     */
    public Object getObject(CacheModel cacheModel, Object key) {
     Object result ;
        String ckey = getKey(cacheModel, key);
        try {
            result = cache.get(ckey);
            if(cacheSize>0){//如果缓存大小有限制,则使用LRU算法
                keyList.remove(ckey);
                if (result != null) {
                  keyList.add(ckey);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();   
            return null;   
        }
        log.info("       getObject memcache!!");
        return result ;
    }   
  
    /**
     * 如果超过缓存大小限制,移出栈底数据
     */
    public void putObject(CacheModel cacheModel, Object key, Object object) {   
        String ckey = getKey(cacheModel, key);   
        keyList.add(ckey);
        try {
         Date expiry = new Date();//过期时间
         expiry.setTime(expiry.getTime()+cacheModel.getFlushInterval());
            cache.add(ckey,object,expiry);
            log.debug("       putObject memcache!");
            log.info("cacheSize:"+cacheSize+",keyListSize:"+keyList.size());
            if (cacheSize>0 && keyList.size() > cacheSize) {   
                String oldestKey = keyList.remove(0);   
                cache.delete(oldestKey);   
                log.debug("       keyList.oldestKey memcache!");
            }   
        } catch (Exception e) {   
            e.printStackTrace();   
        }   
  
    }   
  
    public Object removeObject(CacheModel cacheModel, Object key) {
        log.info("       removeObject memcache!");
        String ckey = getKey(cacheModel, key);   
        try {   
            if (keyList.contains(ckey)) {
             keyList.remove(ckey);
                log.info("       removeObject memcache!");
                return cache.delete(ckey);   
            }   
        } catch (Exception e) {   
            e.printStackTrace();   
        }   
        return null;   
    }   
    public void setProperties(Properties props){
        String size = props.getProperty("size");   
        if (size == null) {
            size = props.getProperty("cache-size");   
        }   
        if (size != null) {
            cacheSize = Integer.parseInt(size);   
        }
        if(cache!=null)
         cache =  MemCachedManager.getInstance();
    }
   
    public int getCacheSize() {
        return cacheSize;
      }
      public void setCacheSize(int cacheSize) {
        this.cacheSize = cacheSize;
      }
      
    private String getKey(CacheModel cacheModel, Object cacheKey) {   
        String key = cacheKey.toString();   
        int keyhash = key.hashCode();   
        String cacheId = cacheModel.getId();
        key = Config.MEMCACHED_PRE + cacheId + "_" + keyhash;
        return key;
    }
}
对于分布式/集群环境下,每个实例的key会不一至,也就是说A机器存入的一条SQL的KEY在B机器里算出来的KEY会不一至。原因是ibatis在key的算法里,把statement id的hash码做为key的一部分,所以需要重写CachingStatement类,修改baseCacheKey(下文高亮色的地方):
/*
*  Copyright 2004 Clinton Begin
*
*  Licensed under the Apache License, Version 2.0 (the "License");
*  you may not use this file except in compliance with the License.
*  You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
*  Unless required by applicable law or agreed to in writing, software
*  distributed under the License is distributed on an "AS IS" BASIS,
*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*  See the License for the specific language governing permissions and
*  limitations under the License.
*/
package com.ibatis.sqlmap.engine.mapping.statement;
import com.ibatis.sqlmap.client.event.RowHandler;
import com.ibatis.sqlmap.engine.cache.CacheKey;
import com.ibatis.sqlmap.engine.cache.CacheModel;
import com.ibatis.sqlmap.engine.mapping.parameter.ParameterMap;
import com.ibatis.sqlmap.engine.mapping.result.ResultMap;
import com.ibatis.sqlmap.engine.mapping.sql.Sql;
import com.ibatis.sqlmap.engine.scope.StatementScope;
import com.ibatis.sqlmap.engine.transaction.Transaction;
import java.sql.SQLException;
import java.util.List;
/**
*
* @modify langnke93
* statement.setBaseCacheKey(0);
* baseCacheKey使用固定值,保证每个实例的CacheKey一至
*
*/
public class CachingStatement extends MappedStatement {
  private MappedStatement statement;
  private CacheModel cacheModel;
  public CachingStatement(MappedStatement statement, CacheModel cacheModel) {
    this.statement = statement;
    this.cacheModel = cacheModel;
  }
  public String getId() {
    return statement.getId();
  }
  public StatementType getStatementType() {
    return statement.getStatementType();
  }
  public Integer getResultSetType() {
    return statement.getResultSetType();
  }
  public Integer getFetchSize() {
    return statement.getFetchSize();
  }
  public ParameterMap getParameterMap() {
    return statement.getParameterMap();
  }
  public ResultMap getResultMap() {
    return statement.getResultMap();
  }
  public int executeUpdate(StatementScope statementScope, Transaction trans, Object parameterObject)
      throws SQLException {
    int n = statement.executeUpdate(statementScope, trans, parameterObject);
    return n;
  }
  public Object executeQueryForObject(StatementScope statementScope, Transaction trans, Object parameterObject, Object resultObject)
      throws SQLException {
    CacheKey cacheKey = getCacheKey(statementScope, parameterObject);
    cacheKey.update("executeQueryForObject");
    Object object = cacheModel.getObject(cacheKey);
    if (object == CacheModel.NULL_OBJECT){
     // This was cached, but null
     object = null;
    }else if (object == null) {
       object = statement.executeQueryForObject(statementScope, trans, parameterObject, resultObject);
       cacheModel.putObject(cacheKey, object);
    }
    return object;
  }
  public List executeQueryForList(StatementScope statementScope, Transaction trans, Object parameterObject, int skipResults, int maxResults)
      throws SQLException {
    CacheKey cacheKey = getCacheKey(statementScope, parameterObject);
    cacheKey.update("executeQueryForList");
    cacheKey.update(skipResults);
    cacheKey.update(maxResults);
    Object listAsObject = cacheModel.getObject(cacheKey);
    List list;
    if(listAsObject == CacheModel.NULL_OBJECT){
      // The cached object was null
      list = null;
    }else if (listAsObject == null) {
      list = statement.executeQueryForList(statementScope, trans, parameterObject, skipResults, maxResults);
      cacheModel.putObject(cacheKey, list);
    }else{
      list = (List) listAsObject;
    }
    return list;
  }
  public void executeQueryWithRowHandler(StatementScope statementScope, Transaction trans, Object parameterObject, RowHandler rowHandler)
      throws SQLException {
    statement.executeQueryWithRowHandler(statementScope, trans, parameterObject, rowHandler);
  }
  public CacheKey getCacheKey(StatementScope statementScope, Object parameterObject) {
statement.setBaseCacheKey(0);//去掉取于statement id 动态的baseCacheKey使用固定值,保证每个实例的CacheKey一至,用于集群环境
    CacheKey key = statement.getCacheKey(statementScope, parameterObject);
    if (!cacheModel.isReadOnly() && !cacheModel.isSerialize()) {
      key.update(statementScope.getSession());
    }
    return key;
  }
  public void setBaseCacheKey(int base) {
    statement.setBaseCacheKey(base);
  }
  public void addExecuteListener(ExecuteListener listener) {
    statement.addExecuteListener(listener);
  }
  public void notifyListeners() {
    statement.notifyListeners();
  }
  public void initRequest(StatementScope statementScope) {
    statement.initRequest(statementScope);
  }
  public Sql getSql() {
    return statement.getSql();
  }
  public Class getParameterClass() {
    return statement.getParameterClass();
  }
  public Integer getTimeout() {
    return statement.getTimeout();
  }
  public boolean hasMultipleResultMaps() {
    return statement.hasMultipleResultMaps();
  }
  public ResultMap[] getAdditionalResultMaps() {
    return statement.getAdditionalResultMaps();
  }
}
好了,这样就可以在map配置里引用刚才的cache实现类
<cacheModel id="CacheVideoInfo"  readOnly="false" serialize="true" type="com.woyo.upload.kernel.util.MemcachedIbatisController">
      <flushInterval seconds="3600"/>
      <property name="size" value="1000" />
    </cacheModel>
<statement id="updateVideoInfo" parameterClass="com.woyo.upload.kernel.entity.VideoInfo" cacheModel="CacheVideoInfo">  ....
目前清除缓存仅依赖于缓存过期,分实例/条件清除缓存还需要做扩展

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-108080-1-1.html 上篇帖子: Memcached--分布式缓存 下篇帖子: 跟我学分布式缓存系统Memcached(一)
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表