一、XMemcached简介

XMemcached是一个新java memcachedclient。也许你还不知道memcached是什么?可以先看看这里。简单来说,Memcached 是一个高性能的分布式内存对象的key-value缓存系统,用于动态Web应用以减轻数据库负载,现在也有很多人将它作为内存式数据库在使用,memcached通过它的自定义协议与客户端交互,而XMemcached就是它的一个java客户端实现。

Memcached的java客户端已经存在两个了:官方提供的基于传统阻塞io由Greg Whalin维护的客户端、Dustin Sallings实现的基于java nio的Spymemcached。另外还有一些在此基础上的改进版本。相比于这些客户端,XMemcached有什么优点呢?或者说,它的主要特性有哪些?

二、XMemcached的主要特性

1、 高性能。XMemcached同样是基于java nio的客户端,java nio相比于传统阻塞io模型来说,有效率高(特别在高并发下)和资源耗费相对较少的优点。传统阻塞IO为了提高效率,需要创建一定数量的连接形成连接池,而nio仅需要一个连接即可(当然,nio也是可以做池化处理),相对来说减少了线程创建和切换的开销,这一点在高并发下特别明显。因此XMemcached与Spymemcached在性能都非常优秀,在某些方面(存储的数据比较小的情况下)Xmemcached比Spymemcached的表现更为优秀,具体可以看这个Java Memcached ClientsBenchmark。

2、 支持完整的协议。Xmemcached支持所有的memcached协议,包括1.4.0开始使用的二进制协议。

3、 支持客户端分布。Memcached的分布只能通过客户端来实现,XMemcached实现了此功能,并且提供了一致性哈希(consistent hash)算法的实现。

4、 允许设置节点权重。XMemcached允许通过设置节点的权重来调节memcached的负载,设置的权重越高,该memcached节点存储的数据将越多,所承受的压力越大。

5、 动态增删节点。XMemcached允许通过JMX或者代码编程实现节点的动态添加或者移除,方便用户扩展和替换节点等。

6、 支持JMX。XMemcached通过JMX暴露的一些接口,支持client本身的监控和调整,允许动态设置调优参数、查看统计数据、动态增删节点等。

7、 与Spring框架和Hibernate-memcached的集成。鉴于很多项目已经使用Spring作为IOC容器,因此XMemcached也提供了对Spring框架的集成支持。Hibernate-memcached是一个允许将memcached作为hibernate的二级缓存的开源项目,默认是使用Spymemcached,Xmemcached提供了对这个项目的支持,允许替换Spymemcached.

8、 客户端连接池。刚才已经提到javanio通常对一个memcached节点使用一个连接,而XMemcached同样提供了设置连接池的功能,对同一个memcached可以创建N个连接组成连接池来提高客户端在高并发环境下的表现,而这一切对使用者来说却是透明的。启用连接池的前提条件是保证数据之间的独立性或者数据更新的同步,对同一个节点的各个连接之间是没有做更新同步的,因此应用需要保证数据之间是相互独立的或者全部采用CAS更新来保证原子性。

9、 可扩展性。XMemcached是基于javanio框架yanf4j实现的,因此在实现上结构相对清楚,分层比较明晰,下面是XMemcached的主要类的UML图。

三、使用指南

在简单介绍完XMemcached的主要特性之后,我们将进入XMemcached的使用环节,这里将按照从简单到复杂的顺序讲解一些例子,以方便用户深入了解XMemcached的使用。

3.1简单例子

对于用户来说,最主要的功能是存取数据,假设我们有一个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 operationtimeout");

          e.printStackTrace();

      } catch (InterruptedException e) {

          // ignore

      }

      try {

          memcachedClient.shutdown();

      } catch (IOException e) {

          System.err.println("Shutdown MemcachedClient fail");

          e.printStackTrace();

      }

因为XMemcachedClient的创建有比较多的可选项,因此提供了一个XMemcachedClientBuilder用于构建MemcachedClient。MemcachedClient是主要接口,操作memcached的主要方法都在这个接口里,XMemcachedClient是它的一个实现。传入的memcached节点列表要求是类似”host1:port1host2:port2 …”这样的字符串,通过AddrUtil.getAddresses方法获取实际的IP地址列表。存储数据是通过set方法,它有三个参数,第一个是存储的key名称,第二个是expire时间(单位秒),超过这个时间,memcached将这个数据替换出去,0表示永久存储(默认是一个月),第三个参数就是实际存储的数据,可以是任意的java可序列化类型。获取存储的数据是通过get方法,传入key名称即可。如果要删除存储的数据,这是通过delete方法,它也是接受key名称作为参数。XMemcached由于是基于nio,因此通讯过程本身是异步的,client发送一个请求给memcached,你是无法确定memcached什么时候返回这个应答,客户端此时只有等待,因此还有个等待超时的概念在这里。客户端在发送请求后,开始等待应答,如果超过一定时间就认为操作失败,这个等待时间默认是一秒,上面例子展现的3个方法调用的都是默认的超时时间,这三个方法同样有允许传入超时时间的重载方法,例如

Value=client.get(“hello”,3000);

就是等待3秒超时,如果3秒超时就跑出TimeutException,用户需要自己处理这个异常。因为等待是通过调用CountDownLatch.await(timeout)方法,因此用户还需要处理中断异常InterruptException。最后的MemcachedException表示Xmemcached内部发生的异常,如解码编码错误、网络断开等等异常情况。

3.2CAS操作

Memcached是通过cas协议还实现原子更新,所谓原子更新就是compare andset,原理类似乐观锁,每次请求存储某个数据同时要附带一个cas值,memcached比对这个cas值与当前存储数据的cas值是否相等,如果相等就让新的数据覆盖老的数据,如果不相等就认为更新失败,这在并发环境下特别有用。XMemcached提供了对CAS协议的支持(无论是文本协议还是二进制协议),CAS协议其实是分为两个步骤:获取CAS值和尝试更新,因此一个典型的使用场景如下:

GetsResponse<Integer> result= client.gets("a");

long cas = result.getCas();

if (!client.cas("a", 0, 2, cas)) {

      System.err.println("cas error");

}

首先通过gets方法获取一个GetsResponse,此对象包装了存储的数据和cas值,然后通过cas方法尝试原子更新,如果失败打印”cas error”。显然,这样的方式很繁琐,并且如果你想尝试多少次原子更新就需要一个循环来包装这一段代码,因此XMemcached提供了一个CASOpertion接口包装了这部分操作,允许你尝试N次去原子更新某个key存储的数据,无需显式地调用gets获取cas值,上面的代码简化为:

client.cas("a", 0, new CASOperation<Integer>(){

             public int getMaxTries() {

                 return 1;

             }

             public Integer getNewValue(long currentCAS, Integer currentValue) {

                 return 2;

             }

          });

CASOpertion接口只有两个方法,一个是设置最大尝试次数的getMaxTries方法,这里是尝试一次,如果尝试超过这个次数将抛出一个TimeoutException,如果你想无限尝试,可以将返回值设定为Integer.MAX_VALUE;另一个方法是根据当前获得的GetsResponse来决定更新数据的getNewValue方法,如果更新成功,这个方法返回的值将存储成功,这个方法的两个参数是最新一次gets返回的GetsResponse结果。

3.3更全面的例子

一些更全面的例子,展现了MemcachedClient接口的主要方法:

MemcachedClientBuilder builder =new XMemcachedClientBuilder(

                 AddrUtil.getAddresses(“localhost:12000”));

            MemcachedClient client = builder.build();

          client.flushAll();

if (!client.set("hello", 0, "world")) {

             System.err.println("set error");

          }

          if (client.add("hello", 0, "dennis")) {

             System.err.println("Add error,key is existed");

         }

          if (!client.replace("hello", 0, "dennis")) {

             System.err.println("replace error");

          }

          client.append("hello", " good");

          client.prepend("hello", "hello ");

          String name = client.get("hello", new StringTranscoder());

          System.out.println(name);

         client.deleteWithNoReply(“hello”);

首先存储了hello对应的world字符串,然后调用add和replace方法去尝试添加和替换,因为数据已经存在,因此add会失败,同样replace在数据存在的情况才会成功,也就是将hello对应的数据更新为dennis,然后通过append和prepend方法在dennis前后加上了字符串hello和good,因此通过get返回的结果是hello dennisgood。而删除数据则是通过deleteWithNoReply方法,这个方法删除数据并且告诉memcached不用返回应答,因此这个方法不会等待应答直接返回,特别适合于批量处理;同样地,set、add、replace等方法也有相应的withNoReply重载版本,具体请看API文档。

下面这个例子展现了incr/decr操作的使用,两个操作类似java中的原子类如AtomicIntger,用于原子递增或者递减变量数值:

assert(1==this.memcachedClient.incr("a", 5, 1));

assert(6==this.memcachedClient.incr("a", 5));

assert(10==this.memcachedClient.incr("a", 4));

assert(9==this.memcachedClient.decr("a", 1));

assert(7==this.memcachedClient.deccr("a", 2));

incr和decr都有三个参数的方法,第一个参数指定递增的key名称,第二个参数指定递增的幅度大小,第三个参数指定当key不存在的情况下的初始值。两个参数的重载方法省略了第三个参数,默认指定为0。

Memcached提供了统计协议用于查看统计信息:

Map<InetSocketAddress,Map<String,String>>result=client.getStats();

getStats方法返回一个map,其中存储了所有已经连接并且有效的memcached节点返回的统计信息,你也可以统计具体的项目,如统计items项目:

Map<InetSocketAddress,Map<String,String>>result=client.getStatsByItem(“items”);

只要向getStatsByItem传入需要统计的项目名称即可。

【转载】Xmemcached用户指南的更多相关文章

  1. 【Flume NG用户指南】(1)设置

    作者:周邦涛(Timen) Email:zhoubangtao@gmail.com 转载请注明出处:  http://blog.csdn.net/zhoubangtao/article/details ...

  2. Android官方技术文档翻译——Gradle 插件用户指南(1-3)

    不知道是什么网络问题,上午一直发不了博客,其它页面基本正常,就是在写博客这里,每次打开都是响应超时.刚才用了VPN,顺便试了一下,竟然能够编辑.想是CDN之类的问题吧. 这次翻译的是Gradle 插件 ...

  3. 【Flume NG用户指南】(2)构造

    作者:周邦涛(Timen) Email:zhoubangtao@gmail.com 转载请注明出处:  http://blog.csdn.net/zhoubangtao/article/details ...

  4. Android官方技术文档翻译——Gradle 插件用户指南(5)

    昨晚把第五章未译完的几句话攻克了.只是第六章没怎么译,明后天又是周末,假设周一前第六章翻译完的话,周一再发第六章. 本文译自Android官方技术文档<Gradle Plugin User Gu ...

  5. Gradle 1.12用户指南翻译——第二十一章. Gradle 插件

    昨天晚上只顾着和女朋友看<匆匆那年>电视剧的最后几集,所以说好的Android文档<Gradle 插件用户指南>第五章自然也没翻译多少.所以今天也发不了第五章的翻译了,就发几个 ...

  6. Android官方技术文档翻译——Gradle 插件用户指南(4)

    最近赶项目,白天基本没时间,只有晚上在家的时候才能看一看.昨天晚上只翻译完了第四章,今天就只发第四章吧. 本文译自Android官方技术文档<Gradle Plugin User Guide&g ...

  7. Android官方技术文档翻译——Gradle 插件用户指南(7)

    本文译自Android官方技术文档<Gradle Plugin User Guide>,原文地址:http://tools.android.com/tech-docs/new-build- ...

  8. Android官方技术文档翻译——Gradle 插件用户指南(6)

    没想到翻译这篇<Gradle 插件用户指南>拖了差不多一个月,还跨年了.不过还好,在2号时终于一口气把剩下的给翻译完了(其实那天剩下的也就不到一章). 今天先发一下第六章,明天再发第七章. ...

  9. Gradle 1.12用户指南翻译——第二十二章. 标准的 Gradle 插件

    其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Github上的地址: https://g ...

随机推荐

  1. Element-ui组件--pagination分页的使用

    一般在写前端页面时,经常会遇到分页这样的效果,element-ui中便有这样的插件,用vue框架使用的很方便,在此做一总结: <template> <div class=" ...

  2. golang vue nginx

    https://segmentfault.com/a/1190000012780963 https://blog.csdn.net/qq_32340877/article/details/790321 ...

  3. 页面加载通过javascript来修改控件属性

    function changeFormElementStatus(tagNames) {            var tagNameArr = tagNames.split("," ...

  4. PHP-输入账号密码进入网页

    1. 2. 3. 4.(itcast.html步骤)随便一个本地网页代替即可

  5. Unity 获取坐标函数 坐标转换函数

    获取世界坐标:transform.position 获取本地坐标:transform.localPosition 获取鼠标坐标:Input.mousePosition 获取手指触摸区域坐标:Input ...

  6. 快速掌握ajax!

    ajax是什么? ajax——asynchronous JavaScript and xml:异步的js和xml 它能使用js访问服务器,而且是异步访问 服务器给客户端的响应一般是整个页面,一个htm ...

  7. ubantu上搭建hive环境

    上次我们在ubantu上安装了hadoop,那我们现在再进一步,开始我们的学习之旅--hive板块吧! 第一步:准备! 软件: I.一个搭建好的hadoop环境 II.hive的bin文件(前往apa ...

  8. 路飞学城Python-Day31

    19-生产者消费者模型 生产者:生成数据的任务 消费者:处理数据的任务 在并发编程的过程中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理,才能继续生产数据:同样的,如果 ...

  9. [luogu2592 ZJOI2008] 生日聚会 (计数dp)

    题目描述 今天是hidadz小朋友的生日,她邀请了许多朋友来参加她的生日party. hidadz带着朋友们来到花园中,打算坐成一排玩游戏.为了游戏不至于无聊,就座的方案应满足如下条件: 对于任意连续 ...

  10. freeswitch 一些坑

    1.You must install libopus-dev to build mod_opus. Stop. 确实已经安装libopus-dev后 将文件 usr/local/src/mod/cod ...