一,Memcached介绍
memcached是被广泛使用的分布式缓存技术,使用它的目的是,通过它缓存数据库查询结果,减少数据库访问次数,提高动态Web应用的速度。
memcached作为分布式缓存服务器,具有如下的特点:
(1)协议简单:
memcached的服务器和客户端使用的通信协议是简单的基于文本行的协议,因此我们可以通过telnet也能在memcached服务器上保存/获取数据,如下:
(2)基于libevent的事件处理:
libevent是个程序库,memcached通过libevent能在linux、BSD等操作系统上发挥其高性能的特性。
(3)内置内存存储方式:
为了提高性能,memcached将数据保存在其内置的内存存储空间里,同样也是因为如此,所以当memcached重启、重启操作系统等操作会导致memcached存储的数据丢失。
另外,当内容的容量达到指定的值后,memcached会使用LRU算法自动删除不使用的缓存,以释放空间。
(4)memcached不互相通信的分布式:
经常说到memcached是分布式的缓存技术,但是它的分布式不是指服务器端的分布式,因为服务器端并没有分布式的功能。它的分布式是通过客户端来实现的,见下图
二,Windows下memcached的使用
1, 用到的jar包
其中Memcached-Java-Client-3.0.0.jar为memcached客户端,jar和源码下载地址http://search.maven.org/。
2, 部分代码
public class MemcachedTest {
protected static MemCachedClient mcc = new MemCachedClient();
protected static MemcachedTest memcachedTest = new MemcachedTest();
//设置与memcached服务器的连接信息
static{
/// 服务器列表和其权重
String [] servers = {"127.0.0.1:11211","192.168.0.101:11211"};
Integer [] weights = {5,5};
// 获取sock连接池的实例对象
SockIOPool pool = SockIOPool.getInstance();
pool.setServers(servers);
pool.setWeights(weights);
// 设置初始连接数、最小和最大连接数以及最大处理时间
pool.setInitConn(5);
pool.setMinConn(5);
pool.setMaxConn(100);
pool.setMaxIdle(1000*60*60*6);
// 设置主线程的睡眠时间
pool.setMaintSleep(30);
// 设置TCP的参数,连接超时等
pool.setNagle(false);
pool.setSocketTO(3000);
pool.setSocketConnectTO(0);
pool.setHashingAlg(SockIOPool.CONSISTENT_HASH);
// 初始化连接池
pool.initialize();
}
/**
* 保护型构造方法,不允许实例化!
*
*/
protected MemcachedTest() {
}
/**
* 获取唯一实例.
*
* @return
*/
public static MemcachedTest getInstance() {
return memcachedTest;
}
/**
* 添加数据
* @param key
* @param value
* @return
*/
public boolean add(String key, String value){
return mcc.add(key, value);
}
public boolean add(String key, String value, Date expiry){
return mcc.add(key, value, expiry);
}
public boolean replace(String key, String value){
return mcc.replace(key,value);
}
public boolean replace(String key, String value, Date expiry){
return mcc.replace(key, value, expiry);
}
public Object get(String key){
return mcc.get(key);
}
public Object flush(){
return mcc.flushAll();
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
MemcachedTest mt = MemcachedTest.getInstance();
mt.flush();
for (int i = 0; i < 10; i ++){
mt.add("hello"+ i, "world" + i);
}
for(int i = 0; i < 10; i ++){
System.out.println("the value of key Hello" + i +" is: " + mt.get("hello" + i));
}
}
}
首先,我们需要一个memcachedClient对象,该对象实现将数据添加到memcached服务器或者从服务器读取数据。然后初始化一个SockIOPool对象,该对象用来维护一个到memcached服务器的持续的连接池,连接池必须在使用前初始化。
3, memcached Client源码解读
在将数据添加到服务器这个过程,主要涉及到了客户端的下面的几个类:
MemCachedClient:生成客户端的入口
AscIIClient: memcachedClient的子类,负责具体的client的实例化
SchoonerSockIOPool
SockIOPool
(1) 初始化一个memcached client对象
protected static MemCachedClient mcc = new MemCachedClient();
查看client的源码发现它会通过解析参数,生成一个子类AscIIClient的对象。
/**
* Creates a new instance of MemCachedClient.
*/
public MemCachedClient() {
this(null, true, false);
}
/**
* Create memcached client.
*
* @param poolName
* name of SockIOPool
* @param isTCP
* use tcp protocol
* @param binaryProtocal
* use binary protocol.
*/
public MemCachedClient(String poolName, boolean isTcp, boolean binaryProtocal) {
if (binaryProtocal)
client = new BinaryClient(poolName);
else
client = isTcp ? new AscIIClient(poolName) : new AscIIUDPClient(poolName);
}
(2)在子类AscIIClient中,定义了一个pool的实例如下:
// pool instance
private SchoonerSockIOPool pool;
根据上面第一点,在生成memcached client实例时,其实实际上生成的AscIIClient的实例,如下:
/**
* Creates a new instance of MemCachedClient accepting a passed in pool
* name.
*
* @param poolName
* name of SockIOPool
* @param binaryProtocal
* whether use binary protocol.
*/
public AscIIClient(String poolName) {
super((MemCachedClient) null);
this.poolName = poolName;
init();
}
其中init()方法主要是完成一个默认的(default)client的初始化。
/**
* Initializes client object to defaults.
*
* This enables compression and sets compression threshhold to 15 KB.
*/
private void init() {
this.sanitizeKeys = true;
this.primitiveAsString = false;
this.compressEnable = false;
this.compressThreshold = COMPRESS_THRESH;
this.defaultEncoding = "UTF-8";
this.poolName = (this.poolName == null) ? "default" : this.poolName;
// get a pool instance to work with for the life of this instance
this.pool = SchoonerSockIOPool.getInstance(poolName);
}
(3)SockIOPool和SchoonerSockIOPool的关系:
在SockIOPool中,定义了一个SchoonerSockIOPool的实例,在初始化SockIOPool的时候,同时会获取一个SchoonerSockIOPool的对象。
// 获取sock连接池的实例对象
SockIOPool pool = SockIOPool.getInstance();
/**
* Single argument version of factory used for back compat. Simply creates a
* pool named "default".
*
* @return instance of SockIOPool
*/
public static SockIOPool getInstance() {
SockIOPool whalinSockIOPool = new SockIOPool();
whalinSockIOPool.schoonerSockIOPool = SchoonerSockIOPool.getInstance("default");
return whalinSockIOPool;
}
(4) 通过SchoonerSockIOPool.getInstance方法获取一个SchoonerSockIOPool的实例时,程序会先检查连接池里面是否有名称为“default”的pool,若有,则取处理返回; 若无,生成名称为“default”的pool,并将该pool放置到连接池里面。
/**
* Factory to create/retrieve new pools given a unique poolName.
*
* @param poolName
* unique name of the pool
* @return instance of SockIOPool
*/
public static SchoonerSockIOPool getInstance(String poolName) {
SchoonerSockIOPool pool;
synchronized (pools) {
if (!pools.containsKey(poolName)) {
pool = new SchoonerSockIOPool(true);
pools.putIfAbsent(poolName, pool);
}
}
return pools.get(poolName);
}