变更历史
2010-06-22 添加客户端分布和SASL验证两节,更新spring配置一节。 2010-06-23 添加maven依赖说明 2010-10-17 1.2.6 released 2011-01-04 1.3 released。添加failure模式和standby节点。
XMemcached简介
XMemcached是一个新java memcached client。也许你还不知道memcached是什么?可以先看看这里。简单来说,Memcached 是一个高性能的分布式内存对象的key-value缓存系统,用于动态Web应用以减轻数据库负载,现在也有很多人将它作为内存式数据库在使用,memcached通过它的自定义协议与客户端交互,而XMemcached就是它的一个java客户端实现。
Memcached的java客户端已经存在两个了:官方提供的基于传统阻塞io由Greg Whalin维护的客户端、Dustin Sallings实现的基于java nio的Spymemcached。另外还有一些在此基础上的改进版本。相比于这些客户端,XMemcached有什么优点呢?或者说,它的主要特性有哪些?
XMemcached的主要特性
高性能
XMemcached同样是基于java nio的客户端,java nio相比于传统阻塞io模型来说,有效率高(特别在高并发下)和资源耗费相对较少的优点。传统阻塞IO为了提高效率,需要创建一定数量的连接形成连接池,而nio仅需要一个连接即可(当然,nio也是可以做池化处理),相对来说减少了线程创建和切换的开销,这一点在高并发下特别明显。因此XMemcached与Spymemcached在性能都非常优秀,在某些方面(存储的数据比较小的情况下)Xmemcached比Spymemcached的表现更为优秀,具体可以看这个Java Memcached Clients Benchmark。
支持完整的协议
Xmemcached支持所有的memcached协议,包括1.4.0正式开始使用的二进制协议。
支持客户端分布
Memcached的分布只能通过客户端来实现,XMemcached实现了此功能,并且提供了一致性哈希(consistent hash)算法的实现。
允许设置节点权重
XMemcached允许通过设置节点的权重来调节memcached的负载,设置的权重越高,该memcached节点存储的数据将越多,所承受的负载越大。
动态增删节点
XMemcached允许通过JMX或者代码编程实现节点的动态添加或者移除,方便用户扩展和替换节点等。
支持JMX
XMemcached通过JMX暴露的一些接口,支持client本身的监控和调整,允许动态设置调优参数、查看统计数据、动态增删节点等。
与Spring框架和Hibernate-memcached的集成
鉴于很多项目已经使用Spring作为IOC容器,因此XMemcached也提供了对Spring框架的集成支持。Hibernate-memcached是一个允许将memcached作为hibernate的二级缓存的开源项目,默认是使用Spymemcached,Xmemcached提供了对这个项目的支持,允许替换Spymemcached.
客户端连接池
刚才已经提到java nio通常对一个memcached节点使用一个连接,而XMemcached同样提供了设置连接池的功能,对同一个memcached可以创建N个连接组成连接池来提高客户端在高并发环境下的表现,而这一切对使用者来说却是透明的。启用连接池的前提条件是保证数据之间的独立性或者数据更新的同步,对同一个节点的各个连接之间是没有做更新同步的,因此应用需要保证数据之间是相互独立的或者全部采用CAS更新来保证原子性。
可扩展性
XMemcached是基于java nio框架yanf4j实现的,因此在实现上结构相对清楚,分层比较明晰,在xmemcached 1.2.5之后已经将yanf4j合并到xmemcached,因此不再需要依赖yanf4j,下面是XMemcached的主要类的UML图:
使用指南
在简单介绍完XMemcached的主要特性之后,我们将进入XMemcached的使用环节,这里将按照从简单到复杂的顺序讲解一些例子,以方便用户深入了解XMemcached的使用。
依赖包
Xmemcached依赖slf4j
在测试下面讲到的代码之前,请先自行下载依赖包或者下载包含了依赖包的xmemcached。
如果你使用maven
如果你使用maven构建你的项目,那么只要添加dependency即可使用xmemcached(仅对1.2.5及以后版本有效)
<dependency> <groupId>com.googlecode.xmemcached</groupId> <artifactId>xmemcached</artifactId> <version>{版本号}</version> </dependency>
简单例子
对于用户来说,最主要的功能是存取数据,假设我们有一个memcached节点IP地址或者域名是host,端口是11211,一个简单的存取数据的例子如下:
MemcachedClientBuilder builder = new XMemcachedClientBuilder( AddrUtil.getAddresses("localhost:11211")); MemcachedClient memcachedClient = builder.build(); try { memcachedClient.set("hello", 0, "Hello,xmemcached"); String value = memcachedClient.get("hello"); System.out.println("hello=" + value); memcachedClient.delete("hello"); value = memcachedClient.get("hello"); System.out.println("hello=" + value); } catch (MemcachedException e) { System.err.println("MemcachedClient operation fail"); e.printStackTrace(); } catch (TimeoutException e) { System.err.println("MemcachedClient operation timeout"); e.printStackTrace(); } catch (InterruptedException e) { // ignore } try { //close memcached client memcachedClient.shutdown(); } catch (IOException e) { System.err.println("Shutdown MemcachedClient fail"); e.printStackTrace(); }
为了节省篇幅,本文的所有代码示例都没有给出完整的package名,具体包名请查询javadoc或者使用IDE工具帮助引入。
因为XMemcachedClient的创建有比较多的可选项,因此提供了一个XMemcachedClientBuilder类用于构建MemcachedClient。MemcachedClient是主要接口,操作memcached的主要方法都在这个接口,XMemcachedClient是它的一个实现。传入的memcached节点列表要求是类似"host1:port1 host2:port2 …"这样的字符串,通过AddrUtil.getAddresses方法获取实际的IP地址列表。
存储数据是通过set方法,它有三个参数,第一个是存储的key名称,第二个是expire时间(单位秒),超过这个时间,memcached将这个数据替换出去,0表示永久存储(默认是一个月),第三个参数就是实际存储的数据,可以是任意的java可序列化类型。获取存储的数据是通过get方法,传入key名称即可。如果要删除存储的数据,这是通过delete方法,它也是接受key名称作为参数。XMemcached由于是基于nio,因此通讯过程本身是异步的,client发送一个请求给memcached,你是无法确定memcached什么时候返回这个应答,客户端此时只有等待,因此还有个等待超时的概念在这里。客户端在发送请求后,开始等待应答,如果超过一定时间就认为操作失败,这个等待时间默认是5秒(1.3.8开始改为5秒,之前是1秒),上面例子展现的3个方法调用的都是默认的超时时间,这三个方法同样有允许传入超时时间的重载方法,例如
value=client.get(“hello”,3000);
就是等待3秒超时,如果3秒超时就跑出TimeutException,用户需要自己处理这个异常。因为等待是通过调用CountDownLatch.await(timeout)方法,因此用户还需要处理中断异常InterruptException。最后的MemcachedException表示Xmemcached内部发生的异常,如解码编码错误、网络断开等等异常情况。
touch更新数据超时时间
经常有这样的需求,就是希望更新缓存数据的超时时间(expire time),在没有touch协议之前,你需要整体的get-set一次:
value=client.get("a"); client.set("a",new-expire-time,value);
两次操作,加上value的序列化/反序列化、网络传输,这个开销可不小。幸好,现在memcached已经支持touch协议,只需要传递key就更新缓存的超时时间:
client.touch(key,new-expire-time);
xmemcached 1.3.6开始支持二进制协议的touch命令,1.3.8开始支持文本协议的touch命令。
有时候你希望获取缓存数据并更新超时时间,这时候可以用getAndTouch方法(仅二进制协议支持):
client.getAndTouch(key,new-expire-time);
客户端分布
Memcached的分布是通过客户端实现的,客户端根据key的哈希值得到将要存储的memcached节点,并将对应的value存储到相应的节点。
XMemcached同样支持客户端的分布策略,默认分布的策略是按照key的哈希值模以连接数得到的余数,对应的连接就是将要存储的节点。如果使用默认的分布策略,你不需要做任何配置或者编程。
XMemcached同样支持一致性哈希(consistent hash),通过编程设置:
MemcachedClientBuilder builder = new XMemcachedClientBuilder(AddrUtil .getAddresses(properties.getProperty("test.memcached.servers")) builder.setSessionLocator(new KetamaMemcachedSessionLocator()); MemcachedClient client=builder.build();
配置的方式请见spring配置一节。