xmemcached主要用法及与Spring集成--总结

一、Memcached Client简要介绍 
Memcached Client目前有3种: 


这三种Client一直存在各种争议: 
  • Memcached Client for Java 比 SpyMemcached更稳定、更早、更广泛;
  • SpyMemcached 比 Memcached Client for Java更高效;
  • XMemcached 比 SpyMemcache并发效果更好。


用数据来说话,参考官方性能对比: 
Memcached Client for Java: https://github.com/gwhalin/Memcached-Java-Client/wiki/PERFORMANCE  
XMemcached: http://xmemcached.googlecode.com/svn/trunk/benchmark/benchmark.html  

二、XMemcached特性  
XMemcached特性: 
  • 高性能
  • 支持完整的memcached文本协议,二进制协议。
  • 支持JMX,可以通过MBean调整性能参数、动态添加/移除server、查看统计等。
  • 支持客户端统计
  • 支持memcached节点的动态增减。
  • 支持memcached分布:余数分布和一致性哈希分布。
  • 更多的性能调整选项。

xMemcached是memcached的一个java客户端,基于java nio,支持memcached的所有协议。本文简要介绍xMemcached的基本用法,以及与Spring的集成配置。

/**
 * XMemcachedClientBuilder是MemcachedClientBuilder的一个实现类,XMemcachedClient是MemcachedClient的一个实现类;
 * 在实际使用中,应该使用接口,利用多态特性,这里直接使用实现类,是为了方便查看实现类的源码。
 */
/**
 * MemcachedClientBuilder builder = new XMemcachedClientBuilder(AddrUtil.getAddresses("192.168.56.200:11211"));
 * MemcachedClient memcachedClient = null;
 */

xMemcached的主要方法示例

MemcachedClient 让spring管理,通过xml设置配置,一下是代码配置,下面有和spring的整合
XMemcachedClientBuilder builder = new XMemcachedClientBuilder(AddrUtil.getAddresses("192.168.56.200:11211 192.168.56.200:11212"));
XMemcachedClient xMemcachedClient = null;

// 注意处理相关异常

// 默认是采用余数哈希,可以修改为一致性哈希
builder.setSessionLocator(new KetamaMemcachedSessionLocator());
// 启用二进制协议,getAndTouch等方法仅在二进制协议中支持
builder.setCommandFactory(new BinaryCommandFactory());

// build the memcached client
xMemcachedClient = (XMemcachedClient) builder.build();

// set: 第一个参数是key,第二个参数是超时时间,第三个参数是value
xMemcachedClient.set("first", 0, "tianjin");//添加或者更新
xMemcachedClient.add("second", 20, "chengdu");//添加,key不存在添加成功返回true,否则返回false
xMemcachedClient.replace("first", 0, "Beijing");//替换,key已经存在替换成功返回true,不存在返回false

// get:根据key获取value
String firstValue = xMemcachedClient.get("first");
String firstValue = xMemcachedClient.get("first",100);//100毫秒没有返回,超时
// delete: 删除item
xMemcachedClient.delete("second");
xMemcachedClient.deleteWithNoReply("third");

// touch:修改item过期时间
xMemcachedClient.touch("first", 0);
firstValue = xMemcachedClient.getAndTouch("first", 10);
logger.info("getAndTouch op, first value: {}", firstValue);

// append, prepend: 追加数据
xMemcachedClient.append("first", ", come on");
xMemcachedClient.prepend("first", "hello ");
logger.info("append and prepend op, first value: {}", xMemcachedClient.get("first"));

/**
 * cas: 通过gets操作返回GetResponse,其中包括value值和cas值
 */
GetsResponse<Integer> result = client.gets("a");
  long cas = result.getCas(); 
  //尝试将a的值更新为2
  if (!client.cas("a", 0, 2, cas)) {
    System.err.println("cas error");
   }
// cas: 重试更方便;第一个方法表示重试次数,第二个方法表示更新的值
xMemcachedClient.cas("first", 0, new CASOperation<String>() {
    @Override
    public int getMaxTries() {
        return 1;//重试一次
    }

    @Override
    public String getNewValue(long currentCAS, String currentValue) {
        return "xian";
    }
});

// incr/decr: 值的增加减少 incr/decr操作的使用,两个操作类似Java中的原子类如AtomicIntger,用于原子递增或者递减变量数值

xMemcachedClient.incr("id", 3, 0);//自增3,初始值0(默认0)
xMemcachedClient.incr("id", 5);
xMemcachedClient.decr("id", 2);//递减最小到0

// namespace: set时指定命名空间,get时也需要指定,可以使命名空间中的所有items失效

String ns = "namespace" ;
this.memcachedClient.withNamespace(ns,
                new MemcachedClientCallable<Void>() {

                    public Void call(MemcachedClient client)
                            throws MemcachedException, InterruptedException,
                            TimeoutException {
                        //a,b,c都在namespace下
                        client.set("a",1);
                        client.set("b",1);
                        client.set("c",1);
                        return null;
                    }
                });
//获取命名空间内的a对应的值
Integer aValue = memcachedClient.withNamespace(ns,
                new MemcachedClientCallable<Integer>() {

                    public Integer call(MemcachedClient client)
                            throws MemcachedException, InterruptedException,
                            TimeoutException {
                                 return client.get("a");
                    }
                });

//使得命名空间失效
memcachedClient.invalidateNamespace(ns);
// stats: 获取统计,也可以根据items获取统计Map<InetSocketAddress, Map<String, String>> stats = xMemcachedClient.getStats();for (InetSocketAddress addr: stats.keySet()) { logger.info("stats map: {}: {}", addr, stats.get(addr).toString());}// flush_all: 是所有item都过期xMemcachedClient.flushAll("192.168.56.200:11211");// 删除一个serverxMemcachedClient.removeServer("192.168.56.200:11212");

xMemcached

XMemcached由于是基于nio,因此通讯过程本身是异步的,client发送一个请求给memcached,你是无法确定memcached什么时候返回这个应答,客户端此时只有等待,因此还有个等待超时的概念在这里。客户端在发送请求后,开始等待应答,如果超过一定时间就认为操作失败,这个等待时间默认是5秒(1.3.8开始改为5秒,之前是1秒),上面例子展现的3个方法调用的都是默认的超时时间,这三个方法同样有允许传入超时时间的重载方法,例如

   value=client.get(“hello”,3000);
还有不需要等待的方法:通过deleteWithNoReply方法,这个方法删除数据并且告诉memcached不用返回应答,因此这个方法不会等待应答直接返回,特别适合于批量处理;同样地,set、add、replace等方法也有相应的withNoReply重载版本

xMemcached与Spring集成

applicationContext.xml的配置:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

    <!--定义一个server-->
    <bean name="server1" class="java.net.InetSocketAddress">
        <constructor-arg><value>localhost</value></constructor-arg>
        <constructor-arg><value>11211</value></constructor-arg>
    </bean>
    <!--定义XMemcachedClientBuilder实例-->
    <bean id="memcachedClientBuilder" class="net.rubyeye.xmemcached.XMemcachedClientBuilder">
        <constructor-arg name="addressList" value="localhost:11211">
        </constructor-arg>
	<!--连接超时-->
<property name="connectTimeout"><value>3000</value></property> <!--SASL验证开启 ,如果没有相关环境,请关闭 start--> <property name="authInfoMap"> <map> <entry key-ref="server1"> <bean class="net.rubyeye.xmemcached.auth.AuthInfo" factory-method="plain"> <constructor-arg name="username"><value>xxx</value></constructor-arg> <constructor-arg name="password"><value>123</value></constructor-arg> </bean> </entry> </map> </property> <!--SASL验证开启 end--> <!--设置线程池--> <property name="connectionPoolSize" value="2"></property> <!--使用二进制协议--> <property name="commandFactory"> <bean class="net.rubyeye.xmemcached.command.BinaryCommandFactory"></bean> </property> <!--设置序列化方式--> <property name="transcoder"> <bean class="net.rubyeye.xmemcached.transcoders.SerializingTranscoder"></bean> </property> <!--设置一致性哈希--> <property name="sessionLocator"> <bean class="net.rubyeye.xmemcached.impl.KetamaMemcachedSessionLocator"></bean> </property> </bean> <!--定义memcachedClient,通过memcachedClientBuilder的build方法--> <bean name="memcachedClient" factory-bean="memcachedClientBuilder" factory-method="build" destroy-method="shutdown"> </bean></beans>

java测试类:

MemcachedClient memcachedClient = null;
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
try {
    memcachedClient = (MemcachedClient) applicationContext.getBean("memcachedClient");
    memcachedClient.set("spring", 0, "3.0.0.RELEASE");
    logger.info("spring: {}", memcachedClient.get("spring"));
    memcachedClient.replace("spring", 0, "4.0.0.RELEASE is coming");
    logger.info("spring, {}", memcachedClient.get("spring"));
} catch (Exception e) {
    logger.info("spring test", e);
}
遇到的坑:不要每次都使用
XMemcachedClientBuilder.build();创建client,内部应该没有做单例,调用多了就会内存溢出,所以自己实现单例
使用下面的配置应该是spring启动时创建client
<!--定义memcachedClient,通过memcachedClientBuilder的build方法-->
    <bean name="memcachedClient" factory-bean="memcachedClientBuilder"
          factory-method="build" destroy-method="shutdown">
    </bean>

如果启动时没有创建成功,最简单的,再手动build一次
@Autowired
private XMemcachedClientBuilder memcachedClientBuilder;
@Autowired
private MemcachedClient memcachedClient;

private MemcachedClient createClient() throws Exception{
    if(memcachedClient==null){//如果spring没有创建成功,再build一次
       return memcachedClient = memcachedClientBuilder.build();
    }
    return null;
}
问题2:默认连接超时connectTimeout 是60000毫秒,如果网络问题连不上服务器,线程会一直请求1分钟,堵塞时间太长,设置连接超时时间
	<!--连接超时-->
<property name="connectTimeout"><value>3000</value></property>

简要总结如下: 
  • Memcached的Key,要杜绝使用空格,且长度控制在250个字符。
  • Memcached的Value,要控制体积,必须小于1MB,必要时进行使用压缩。
  • 失效时间,0为永久有效,最大值不得超过30天(2592000s),否则重新计算可能缓存只有1秒
  • Memcached仅支持LRU算法,完全适用你的需要。
  • 尽量不要将List这种重体积对象扔到Memcached中,传输、存储都会产生瓶颈。
  • 使用一致性哈希算法实现,提高多个Memcacehd Server利用率。
关于使用XMemcached实现时,参考如下实现: 
Java代码   收藏代码
  1. private MemcachedClientBuilder createMemcachedClientBuilder(  
  2.         Properties properties) {  
  3.     String addresses = properties.getProperty(ADDRESSES).trim();  
  4.   
  5.     if (logger.isInfoEnabled()) {  
  6.         logger.info("Configure Properties:[addresses = " + addresses + "]");  
  7.     }  
  8.     MemcachedClientBuilder builder = new XMemcachedClientBuilder(  
  9.             AddrUtil.getAddresses(addresses));  
  10.   
  11.     // 使用二进制文件  
  12.     builder.setCommandFactory(new BinaryCommandFactory());  
  13.     // 使用一致性哈希算法(Consistent Hash Strategy)  
  14.     builder.setSessionLocator(new KetamaMemcachedSessionLocator());  
  15.     // 使用序列化传输编码  
  16.     builder.setTranscoder(new SerializingTranscoder());  
  17.     // 进行数据压缩,大于1KB时进行压缩  
  18.     builder.getTranscoder().setCompressionThreshold(1024);  
  19.   
  20.     return builder;  
  21. }  

主要有以下几点参考: 
  • 使用二进制文件模式
  • 使用一致性哈希算法
  • 使用序列化编码
  • 对数据进行压缩

参考
* https://nkcoder.github.io/2014/02/16/xmemcached-usage-with-spring/ --快速应用
*
* https://github.com/killme2008/xmemcached --官方
相关链接: (写的非常好)
Memcached笔记——(一)安装&常规错误&监控 
Memcached笔记——(二)XMemcached&Spring集成 
Memcached笔记——(三)Memcached使用总结 
Memcached笔记——(四)应对高并发攻击 

2016-11-16 追加:

1:使用incr原子性 自增发送红包,应对高并发
2:使用add 作为分布式锁
坑1:set 设置过期时间如果超过3个月,接口返回成功,其实没有存上
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
spring+xmemcached aop切面 需要xmemcached-1.2.5+spring-2.5.6 <bean name="factoryMemcachedClient" class="net.rubyeye.xmemcached.utils.XMemcachedClientFactoryBean" destroy-method="shutdown"> <property name="servers"> <value>${XMemcached_servers}</value> </property> <!-- server's weights --> <property name="weights"> <list> <value>1</value> <value>2</value> <value>3</value> </list> </property> <!-- nio connection pool size --> <property name="connectionPoolSize" value="${XMemcached_connectionPoolSize}"></property> <!-- Use binary protocol,default is TextCommandFactory, BinaryCommandFactory KestrelCommandFactory --> <property name="commandFactory"> <bean class="net.rubyeye.xmemcached.command.BinaryCommandFactory"></bean> </property> <!-- Distributed strategy KetamaMemcachedSessionLocator--> <property name="sessionLocator"> <bean class="net.rubyeye.xmemcached.impl.KetamaMemcachedSessionLocator"></bean> </property> <!-- Serializing transcoder --> <property name="transcoder"> <bean class="net.rubyeye.xmemcached.transcoders.SerializingTranscoder" /> </property> <!-- ByteBuffer allocator --> <property name="bufferAllocator"> <bean class="net.rubyeye.xmemcached.buffer.SimpleBufferAllocator"></bean> </property> </bean> <bean id="cachingClient" class="com.dmx.cache.caching.impl.CachingXMemcachedClient" init-method="init" destroy-method="destroy"> <property name="memcachedClient" ref="factoryMemcachedClient" /> <property name="isflushAll" value="${XMemcached_isflushAll}" /> </bean> <bean id="cachingManager" class="com.dmx.cache.caching.impl.CachingManagerImpl"> <property name="cachingClient" ref="cachingClient" /> </bean> <bean id="cacheBeforeAdvice" class="com.dmx.cache.interceptor.advice.CacheBeforeAdvice"></bean> <bean id="cacheAfterAdvice" class="com.dmx.cache.interceptor.advice.CacheAfterAdvice"></bean> <bean id="cacheInterceptor" class="com.dmx.cache.interceptor.CacheInterceptor"> <property name="cachingManager" ref="cachingManager" /> <property name="cacheAttributes"> <props> <prop key="save*">update</prop> <prop key="remove*">update</prop> <prop key="add*">update</prop> <prop key="del*">update</prop> <prop key="get*">readOnly</prop> <prop key="getPlaybill">readOnly=10</prop> </props> </property> </bean> <bean id="cacheProxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="interceptorNames"> <list> <value>cacheBeforeAdvice</value> <value>cacheAfterAdvice</value> <value>cacheInterceptor</value> </list> </property> </bean> <bean id="demoDao" parent="cacheProxyFactoryBean"> <property name="target" ref="demoDaotarget" /> </bean>

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值