为Jpetstore增加memcached缓存支持

Jpetstore是一个典型的web应用,其开发框架为struts(spring-web)+spring+ibatis,因此以它做为例子有很好的实际意义。

 

本篇的前提是memcached server已经安装并且启动,我们在此只是看看如何使用其java client进行数据的读取和更新,这里所用的client都是比较原始的,没有进行封装。

 

1.memcached client for java

 

1.1从whalin下载jar包,然后安装到nexus中,在jpetstore的pom.xml中增加如下依赖:

   <dependency>
    	<groupId>com.danga</groupId>
    	<artifactId>memcached</artifactId>
    	<version>2.0.1</version>
    	<type>jar</type>
    </dependency>

1.2配置

在dataAccessContext-local.xml中增加如下配置

    <bean id="memcachedPool" class="com.danga.MemCached.SockIOPool" factory-method="getInstance"  
		init-method="initialize" destroy-method="shutDown">  
		<constructor-arg><value>eddyMemcachedPool</value></constructor-arg>  
		<property name="servers">  
		<list>  
		   <value>127.0.0.1:11211</value>
		</list>  
		</property>  
		<property name="initConn"><value>20</value></property>  
		<property name="minConn"><value>10</value></property>  
		<property name="maxConn"><value>50</value></property>  
		<property name="maintSleep"><value>30</value></property>  
		<property name="nagle"><value>false</value></property>  
		<property name="socketTO"><value>3000</value></property>    
	</bean>  
	<bean id="memcachedClient" class="com.danga.MemCached.MemCachedClient">  
		<constructor-arg><value>eddyMemcachedPool</value></constructor-arg>  
		<property name="compressEnable"><value>true</value></property>  
		<property name="compressThreshold"><value>4096</value></property> 
	</bean>

 1.3在dao的实现中增加memcached支持,譬如SqlMapProductDao中作如下改动

增加注入

private MemCachedClient mmc;
	
  public MemCachedClient getMmc() {
    return mmc;
  }
	
  public void setMmc(MemCachedClient mmc) {
    this.mmc = mmc;
  }
 

修改实现代码

public Product getProduct(String productId) throws DataAccessException {
	  String key = "Product-"+productId;
	  Product p = (Product)mmc.get(key);
	  if(p==null){
		  p = (Product) getSqlMapClientTemplate().queryForObject("getProduct", productId);
		  mmc.set(key, p);
		  System.out.println("ddddddddddddddddddddddddddddddddddddddddddddd");
	  }else{
		  System.out.println("mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm");
	  }
	  
	  return p;
  }

 1.4修改spring配置

        <bean id="productDao" class="org.springframework.samples.jpetstore.dao.ibatis.SqlMapProductDao">
		<property name="sqlMapClient" ref="sqlMapClient"/>
		<property name="mmc" ref="memcachedClient"/>
	</bean>

 

修改完毕,第一次访问,可以看到后台打印出

ddddddddddddddddddddddddddddddddddddddddddddd

后面的访问都打印出

mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm

 

2。spymemcached http://code.google.com/p/spymemcached

 

  • An improved Java API maintained by Dustin Sallings.
  • Aggressively optimised, ability to run async, supports binary protocol, etc. See site for details.

  参考simple-spring-memcachedhttp://code.google.com/p/simple-spring-memcached/

 

  2.1为项目增加spymemcached依赖,修改pom.xml,增加:

 

    <dependency>
      <groupId>spy</groupId>
      <artifactId>memcached</artifactId>
      <version>2.3.1</version>
    </dependency>

 2.2添加memcached client factory和memcached helper bean

 

package org.springframework.samples.jpetstore.util;

public class MemcachedConnectionBean {

	private String nodeList;
    private boolean consistentHashing;

    public String getNodeList() {
            return nodeList;
    }

    public void setNodeList(final String nodeList) {
            this.nodeList = nodeList;
    }

    public boolean isConsistentHashing() {
            return consistentHashing;
    }

    public void setConsistentHashing(final boolean consistentHashing) {
            this.consistentHashing = consistentHashing;
    }
}

 

 

 

package org.springframework.samples.jpetstore.util;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.List;

import net.spy.memcached.AddrUtil;
import net.spy.memcached.ConnectionFactory;
import net.spy.memcached.DefaultConnectionFactory;
import net.spy.memcached.KetamaConnectionFactory;
import net.spy.memcached.MemcachedClient;
import net.spy.memcached.MemcachedClientIF;


public class MemcachedClientFactory {

	private MemcachedConnectionBean bean;

    public void setBean(MemcachedConnectionBean bean) {
            this.bean = bean;
    }

    public MemcachedClientIF createMemcachedClient() throws IOException{
            if (this.bean == null) {
                    throw new RuntimeException("The MemcachedConnectionBean must be defined!");
            }
            final List<InetSocketAddress> addrs = AddrUtil.getAddresses(this.bean.getNodeList());
            final ConnectionFactory connectionFactory = this.bean.isConsistentHashing() ?
                            new KetamaConnectionFactory() : new DefaultConnectionFactory();
            final MemcachedClientIF client = new MemcachedClient(connectionFactory, addrs);
            return client;
    }

}
 

 

  2.3 修改dataAccessContext-local.xml,增加如下

<bean id="memcachedConnectionBean" class="org.springframework.samples.jpetstore.util.MemcachedConnectionBean">
        <property name="consistentHashing" value="true" />
        <property name="nodeList" value="127.0.0.1:11211 127.0.0.1:11311" />
    </bean>
	<bean id="memcachedClientFactory" class="org.springframework.samples.jpetstore.util.MemcachedClientFactory" >
        <property name="bean" ref="memcachedConnectionBean" />
    </bean>
    <bean id="memcachedSpyClient" factory-bean="memcachedClientFactory" factory-method="createMemcachedClient" />

   同时修改其中的

<bean id="productDao" class="org.springframework.samples.jpetstore.dao.ibatis.SqlMapProductDao">
		<property name="sqlMapClient" ref="sqlMapClient"/>
		<property name="mmc" ref="memcachedClient"/>
	</bean>

   将mmc换成memcachedSpyClient

 

  2.4修改SqlMapProductDao,将spymemcached client注入

 

  private MemcachedClientIF mmc;
	
  public MemcachedClientIF getMmc() {
    return mmc;
  }
	
  public void setMmc(MemcachedClientIF mmc) {
    this.mmc = mmc;
  }

  2.5修改客户端调用方式

 

public Product getProduct(String productId) throws DataAccessException {
	  String key = "Product-"+productId;
	  Product p = (Product)mmc.get(key);
	  if(p==null){
		  p = (Product) getSqlMapClientTemplate().queryForObject("getProduct", productId);
		  mmc.set(key, 3600, p);



		  System.out.println("ddddddddddddddddddddddddddddddddddddddddddddd");
	  }else{
		  System.out.println("mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm");
	  }
	  
	  return p;
  }
 

 

修改完毕,其实从这个改动来看,主要是获取memcached client有所区别。

 

总结:

        从上面的两个例子可以看出,两种缓存的方式都对代码进行了侵入,所以在更换memcached client时需要修改不少的代码。因此要让缓存客户端对我们的代码透明,就需要做自己的cache接口和cache client以及其factory。

 

 

这两种客户端对于使用各自的set/get没有问题,对于对象为string的互相set/get也没有问题,但是对于复杂object的互相set/get是不行的。譬如,对于对象Product,如下:

import java.io.Serializable;


public class Product implements Serializable {

  /* Private Fields */

  private String productId;
  private String categoryId;
  private String name;
  private String description;

  /* JavaBeans Properties */

  public String getProductId() { return productId; }
  public void setProductId(String productId) { this.productId = productId.trim(); }

  public String getCategoryId() { return categoryId; }
  public void setCategoryId(String categoryId) { this.categoryId = categoryId; }

  public String getName() { return name; }
  public void setName(String name) { this.name = name; }

  public String getDescription() { return description; }
  public void setDescription(String description) { this.description = description; }

  /* Public Methods*/

  public String toString() {
    return getName();
  }

}

 使用第一个客户端set,

Product obj = new Product();
obj.setName("test");
obj.setProductId("1-2-3");
mcc.set("P-1-2-3", obj);

 使用第二个客户端get,

Product bar = (Product)mcc.get( "P-1-2-3" );

 就会产生如下异常:

Exception in thread "main" java.lang.ClassCastException: java.lang.Byte
	at TestMemcached.objexamples(TestMemcached.java:95)
	at TestMemcached.main(TestMemcached.java:112)

 从异常来看是类型转换错误,分别打印出

System.out.println("mcc|"+mcc.get( "P-1-2-3" )+"|");

System.out.println("spy|"+getSpy().get( "P-1-2-3" )+"|");

 结果是

mcc|Product@dd5b|

 和

spy|?? Product??w
categoryIdtxx
 Ljava/lang/String;Lxx
 descriptionqxx
 nameq productIdqxx
 xppptxx ========name========txx
1-2-3|

 xx代表一些特殊字符。

 

很明显,这两个client不兼容,至今没找到解决方案。(?????)

你可能感兴趣的:(spring,bean,.net,ibatis,memcached)