码迷,mamicode.com
首页 > 系统相关 > 详细

ibatis结合memcache实现多tomcat共享缓存

时间:2015-11-27 17:04:24      阅读:436      评论:0      收藏:0      [点我收藏+]

标签:

1、下载xmemcached-1.3.8.jar

2、实现CacheController接口

MemcachedIbatisController.java

package com.xxxxxx.memcached;

import java.text.MessageFormat;
import java.util.Collections;
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.CacheKey;
import com.ibatis.sqlmap.engine.cache.CacheModel;

public class MemcachedIbatisController implements CacheController {
    private static Logger LOG = Logger.getLogger(MemcachedIbatisController.class);

    private MemcachedManager cache;
    private List<String> keyList;
    private int cacheSize;
    private boolean initExpiry = false;
    private int expiry = 0;

    public MemcachedIbatisController() {
        if (LOG.isDebugEnabled()) {
            LOG.info("Enable MemcachedIbatisController !");
        }
        this.cacheSize = 0;
        this.cache = MemcachedProxy.memcachedManager;
        this.keyList = getKeyListInstance();
    }

    public synchronized List<String> getKeyListInstance() {
        if (keyList == null) {
            keyList = Collections.synchronizedList(new LinkedList<String>());
        }
        return keyList;
    }

    public void flush(CacheModel cacheModel) {
        if (LOG.isInfoEnabled()) {
            LOG.info("Flush memcache!");
        }
        String key = null;
        
        try {
            if (keyList.isEmpty()) {
                cache.flushAll();
            } else
                for (int i = 0; i < keyList.size(); i++) {
                    key = keyList.get(i);
                    cache.delete(key);
                }
        } catch (Exception e) {
            LOG.error("MemcachedIbatisController method flush {}", e);
        } finally {
            if (keyList.size() > 0) {
                keyList.clear();
            }
        }
    }

    public Object getObject(CacheModel cacheModel, Object key) {
        Object result;
        String ckey = getKey(cacheModel, key);
        LOG.error(key);
        try {
            result = cache.get(ckey);
            if (cacheSize > 0) {
                keyList.remove(ckey);
                if (result != null) {
                    keyList.add(ckey);
                }
            }
        } catch (Exception e) {
            LOG.error("MemcachedIbatisController method getObject {}", e);
            return null;
        }

        if (LOG.isInfoEnabled()) {
            LOG.info(MessageFormat.format("Get the {0} from memcached, value={1}", ckey, result));
        }

        return result;
    }


    public void putObject(CacheModel cacheModel, Object key, Object object) {
        String ckey = getKey(cacheModel, key);
        keyList.add(ckey);
        try {

            if (!initExpiry) {
                expiry = new Long(cacheModel.getFlushInterval()).intValue() / 1000;
                initExpiry = true;
            }
            
            boolean ret = cache.set(ckey, expiry, object);
            if (LOG.isInfoEnabled()) {
                LOG.info(MessageFormat.format("Add {0} to memcached, returnt state={1}", ckey, ret));
                LOG.info("CacheSize:" + cacheSize + ", keyListSize:" + keyList.size());
            }
            
            if (cacheSize > 0 && keyList.size() > cacheSize) {
                String oldestKey = keyList.remove(0);
                ret = cache.delete(oldestKey);
                if (LOG.isInfoEnabled()) {
                    LOG.info(MessageFormat.format("Remove {0} to memcached, returnt state={1}", ckey, ret));
                }
            }
        } catch (Exception e) {
            LOG.error("MemcachedIbatisController method putObject {}", e);
        }

    }

    public Object removeObject(CacheModel cacheModel, Object key) {
        String ckey = getKey(cacheModel, key);
        try {
            if (keyList.contains(ckey)) {
                keyList.remove(ckey);
                if (LOG.isInfoEnabled()) {
                    LOG.info(MessageFormat.format("Remove {0} from keyList!", ckey));
                }
                boolean ret = cache.delete(ckey);
                if (LOG.isInfoEnabled()) {
                    LOG.info(MessageFormat.format("Delete {0} from memcached, returnt state={1}", ckey, ret));
                }
                return ret;
            }
        } catch (Exception e) {
            LOG.error("MemcachedIbatisController method removeObject {}", e);
        }
        
        return null;
    }

    public void setProperties(Properties props) {
        if (LOG.isDebugEnabled()) {
            LOG.debug("Set Properties");
        }
        
        String size = props.getProperty("cache-size");
        
        if (size != null) {
            cacheSize = Integer.parseInt(size);
        }
    }

    public int getCacheSize() {
        return cacheSize;
    }

    public void setCacheSize(int cacheSize) {
        this.cacheSize = cacheSize;
    }

    private String getKey(CacheModel cacheModel, Object cacheKey) {
        //CacheKey ck = (CacheKey)cacheKey;
        String key = cacheKey.toString();
        int keyhash = key.hashCode();
        String cacheId = cacheModel.getId();
        key = "IBATIS_CACHED" + "_" + cacheId + "_" + keyhash;
        return key;
    }

    @Override
    public void configure(Properties arg0) {
        // do what? sorry i don`t know
    }

}

3、memcache的操作类和代理

MemcachedManager.java

package com.xxxxxx.memcached;

import net.rubyeye.xmemcached.MemcachedClient;

import org.apache.log4j.Logger;


public class MemcachedManager {

    private static Logger LOG = Logger.getLogger(MemcachedManager.class);

    private static MemcachedClient memcachedClient;

    public MemcachedManager() {
        super();
    }

    public boolean set(String key, int expiry, Object o) {
        try {
            return memcachedClient.set(key, expiry, o);
        } catch (Exception e) {
            LOG.error("MemcachedManager method set {}", e);
        }
        return false;
    }

    public Object get(String key) {
        Object result = null;
        try {
            result = memcachedClient.get(key);
        } catch (Exception e) {
            LOG.error("MemcachedManager method get {}", e);
        }
        return result;
    }

    public boolean delete(String key) {
        try {
            return memcachedClient.delete(key);
        } catch (Exception e) {
            LOG.error("MemcachedManager method delete {}", e);
        }
        return false;
    }

    public void flushAll() {
        try {
            memcachedClient.flushAll();
        } catch (Exception e) {
            LOG.error("MemcachedManager method flushAll {}", e);
        }
    }

    public void setMemcachedClient(MemcachedClient memcachedClient) {
        if (null == MemcachedManager.memcachedClient)
            MemcachedManager.memcachedClient = memcachedClient;
    }

}

MemcachedProxy.java

package com.xxxxxx.memcached;

public class MemcachedProxy {
    
    public static MemcachedManager memcachedManager;

    public MemcachedManager getMemcachedManager() {
        return memcachedManager;
    }

    public void setMemcachedManager(MemcachedManager memcachedManager) {
        if (null == MemcachedProxy.memcachedManager)
        MemcachedProxy.memcachedManager = memcachedManager;
    }
    
    

}

4、配置spring的配置文件

该配置一定要写在ibatis配置之前,保证,先初始化(如果使用扫描器注入bean,要写在扫描器之前)

<!-- 注意:memcached 初始化要在ibatis初始化之前,否则找不到memcachedProxy对象. -->
    <!-- memcached 初始化开始 -->
    <bean id="memcachedClientBuilder" class="net.rubyeye.xmemcached.XMemcachedClientBuilder">
        <!-- XMemcachedClientBuilder have two arguments.First is server list,and 
            second is weights array. -->
        <constructor-arg>
            <list>
                <bean class="java.net.InetSocketAddress">
                    <constructor-arg>
                        <value>192.168.1.80</value>
                    </constructor-arg>
                    <constructor-arg>
                        <value>11210</value>
                    </constructor-arg>
                </bean>
            </list>
        </constructor-arg>
        <property name="connectionPoolSize" value="5"></property>
        <property name="commandFactory">
            <bean class="net.rubyeye.xmemcached.command.BinaryCommandFactory"></bean>
        </property>
        <property name="sessionLocator">
            <bean class="net.rubyeye.xmemcached.impl.KetamaMemcachedSessionLocator"></bean>
        </property>
        <property name="transcoder">
            <bean class="net.rubyeye.xmemcached.transcoders.SerializingTranscoder">
                <constructor-arg>
                    <value>102400</value>
                </constructor-arg>
            </bean>
        </property>
    </bean>
    
    <bean id="memcachedClient" factory-bean="memcachedClientBuilder"
        factory-method="build" destroy-method="shutdown" />


    <bean id="memcachedManager" class="com.xxxxxx.memcached.MemcachedManager">
        <property name="memcachedClient">
            <ref bean="memcachedClient"/>
        </property>
    </bean>
    
    <bean id="memcachedProxy" class="com.xxxxxx.memcached.MemcachedProxy">
        <property name="memcachedManager">
            <ref bean="memcachedManager"/>
        </property>
    </bean>
    <!-- memcached 初始化结束 -->

5、ibatis配置文件SqlMapConfig.xml中添加对缓存的支持

<settings cacheModelsEnabled="true"
    enhancementEnabled="true"  
    lazyLoadingEnabled="true"
    />

6、ibatis的sql.xml中使用CacheModel

<cacheModel id="vweb_cache" readOnly="false" serialize="true" type="com.xxxxxx.memcached.MemcachedIbatisController">
        <flushInterval hours="24" />
          <flushOnExecute statement="insertVweb" />
          <flushOnExecute statement="updateVweb" />
          <flushOnExecute statement="removeVweb" />
          <property name="cache-size" value="1000" />
    </cacheModel>
<select id="getVwebList" parameterClass="com.xxxxxx.vweb.model.Vweb" resultClass="com.xxxxxx.vweb.model.Vweb" cacheModel="vweb_cache">
        SELECT * FROM wx_vweb
            <include refid="getVwebList_body" /> 
            <dynamic prepend=""> 
                <isNotNull property="orderCol"> 
                    order by $orderCol$ 
                    <isNotNull property="ascDesc"> 
                        $ascDesc$ 
                    </isNotNull> 
                </isNotNull> 
            </dynamic> 
            <dynamic prepend=""> 
                <isNotNull property="rowNumStart"> 
                    <isNotNull property="pageSize"> 
                        LIMIT #rowNumStart#,#pageSize# 
                    </isNotNull> 
                </isNotNull> 
            </dynamic>
    </select>

7、可以启动tomcat,测试Cache是否生效(注意日志输出)

Get the IBATIS_CACHED_Vweb.vweb_cache_1438835114 from memcached, value=[B@ff0d06

表示缓存已经生效

8、修改ibatis源码,实现多个tomcat共享缓存

原理:memcache 为KV数据库,所以,保证同一条sql语句,同样的查询条件,在缓存时,key值一样,就实现了缓存共享

ibatis的CacheKey生成,不懂的可以百度

com.ibatis.sqlmap.engine.impl.SqlMapExecutorDelegate.java中最终决定CacheKey的代码

  /**
   * Add a mapped statement
   *
   * @param ms - the mapped statement to add
   */
  public void addMappedStatement(MappedStatement ms) {
    if (mappedStatements.containsKey(ms.getId())) {
      throw new SqlMapException("There is already a statement named " + ms.getId() + " in this SqlMap.");
    }
    ms.setBaseCacheKey(hashCode());
    mappedStatements.put(ms.getId(), ms);
  }
ms.setBaseCacheKey(hashCode()); 这句代码决定了basekey的生成
hashCode方法的实现如下
  public int hashCode() {
    CacheKey key = new CacheKey();
    if (txManager != null) {
      key.update(txManager);
      if (txManager.getDataSource() != null) {
        key.update(txManager.getDataSource());
      }
    }
    //key.update(System.identityHashCode(this));
    key.update(DEFAULT_SYS_HASHCODE);
    return key.hashCode();
  }

注释掉的部分为原来的实现,这也就导致了不同的tomcat实例,basekey不一致

把代码进行替换,hashCode生成为固定值,保证多个tomcat中CacheKey一致

实现缓存共享

9,、替换jar包中的class文件,启动多个tomcat验证缓存的共享

ibatis结合memcache实现多tomcat共享缓存

标签:

原文地址:http://www.cnblogs.com/wudicaidou/p/5000926.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!