庄周梦蝶

生活、程序、未来
   :: 首页 ::  ::  :: 聚合  :: 管理

xmemcached发布1.0-BETA版

Posted on 2009-03-09 15:32 dennis 阅读(1930) 评论(1)  编辑  收藏 所属分类: javamy open-source
   xmemcached发布1.0-beta,从0.60直接到1.0-beta,主要改进如下:
1、支持更多协议,在已有协议支持的基础上添加了append、prepend、gets、批量gets、cas协议的支持,具体请查看XMemcachedClient类的实例方法。重点是cas操作,下文将详细描述下。
2、memcached分布支持,支持连接多个memcached server,支持简单的余数分布和一致性哈希分布。
3、0.60版本以来的bug修复。

   memcached 1.2.4之后开始支持cas协议,该协议存储数据同时发送一个版本号,只有当这个版本号与memcached server上该key的最新版本一致时才更新成功,否则返回EXISTS,版本号的获取需要通过gets协议获得,cas全称就是compare and set,如果对hibernate乐观锁和java.util.concurrent.atomic包都比较熟悉的话这个概念应该很了解了。xmemcached 1.0-beta开始支持cas协议,看例子:
XMemcachedClient client = new XMemcachedClient();
client.addServer(
"localhost",11211);
client.set(
"a"01); //设置a为1
GetsResponse result = client.gets("a");
long cas = result.getCas(); //获取当前cas
//尝试更新a成2
if (!client.cas("a"02, cas)) 
    System.err.println(
"cas error");

    XMemcachedClient.cas(final String key, final int exp, Object value, long cas)将尝试更新key的值到value,如果失败就返回false。这样搞好像很麻烦,需要先gets获取cas值,然后再调用cas方法更新,因此XMemcached提供了一个包装类可以帮你搞定这两步,并且提供重试机制:

             /**
             * 合并gets和cas,利用CASOperation
             
*/
            client.cas(
"a"0new CASOperation() {

                @Override
                
public int getMaxTries() {
                    
return 10;
                }

                @Override
                
public Object getNewValue(long currentCAS, Object currentValue) {
                    System.out.println(
"current value " + currentValue);
                    
return 2;
                }

            });
    通过CASOperation,你只要实现两个方法即可,getMaxTries返回最大重试次数,超过这个次数还没有更新成功就抛出TimeoutException;getNewValue方法返回依据当前cas和缓存值,你希望设置的更新值。看一个cas更详细的例子,开100个线程递增缓冲中的变量a,采用cas才能保证最后a会等于100:

import java.util.concurrent.CountDownLatch;

import net.rubyeye.xmemcached.CASOperation;
import net.rubyeye.xmemcached.XMemcachedClient;
/**
 * 测试CAS
 * 
@author dennis
 
*/
class CASThread extends Thread {
    
private XMemcachedClient mc;
    
private CountDownLatch cd;

    
public CASThread(XMemcachedClient mc, CountDownLatch cdl) {
        
super();
        
this.mc = mc;
        
this.cd = cdl;

    }

    
public void run() {
        
try {
            
if (mc.cas("a"0new CASOperation() {
                @Override
                
public int getMaxTries() {
                    
return 50;
                }

                @Override
                
public Object getNewValue(long currentCAS, Object currentValue) {
                    System.out.println(
"currentValue=" + currentValue
                            
+ ",currentCAS=" + currentCAS);
                    
return ((Integer) currentValue).intValue() + 1;
                }

            }))
                
this.cd.countDown();
        } 
catch (Exception e) {
            e.printStackTrace();
        }
    }
}

public class CASTest {
    
static int NUM = 100;

    
public static void main(String[] args) throws Exception {
        XMemcachedClient mc 
= new XMemcachedClient();
        mc.addServer(
"192.168.222.100"11211);
        
// 设置初始值为0
        mc.set("a"00);
        CountDownLatch cdl 
= new CountDownLatch(NUM);
        
// 开NUM个线程递增变量a
        for (int i = 0; i < NUM; i++)
            
new CASThread(mc, cdl).start();

        cdl.await();
        
// 打印结果,最后结果应该为NUM
        System.out.println("result=" + mc.get("a"));
        mc.shutdown();
    }
}

    最高重试次数设置成了50,观察输出你就会知道cas冲突在高并发下非常频繁,这个操作应当慎用。

    说完cas,我们再来看下xmemcached对分布的支持。
1、如何添加多个memcached server?
通过XMemcachdClient.addServer(String ip,int port)方法,
            XMemcachedClient mc = new XMemcachedClient();
            mc.addServer(ip1, port1);
            mc.addServer(ip2, port2);
            mc.addServer(ip3, port3);
            mc.addServer(ip4, port3);

2、怎么分布?
在添加了>=2个memcached server后,对XMemcachdClient的存储、删除等操作都将默认根据key的哈希值连接数的余数做分布,这也是spymemcached默认的分布算法。这个算法简单快速,然而在添加或者移除memcached server后,缓存会大面积失效需要重组,这个代价太高,因此还有所谓Consistent Hashing算法,通过将memcached节点分布在一个0-2^128-1的环上,发送数据到某个节点经过的跳跃次数可以缩减到O(lgn)次,并且在添加或者移除节点时最大限度的降低影响,这个算法的思想其实来源于p2p网络的路由算法,不过路由算法比这个复杂多了,毕竟memcached的分布是在客户端,因此不需要节点之间的通讯和路由表的存储更新等。这个算法在java上的实现可以通过TreeMap红黑树,具体可以参考这里这里
  在xmemcached启动Consistent Hashing如下:

XMemcachedClient client = new XMemcachedClient(new KetamaMemcachedSessionLocator(HashAlgorithm.CRC32_HASH));
client.addServer(ip, 
12000);
client.addServer(ip, 
12001);
client.addServer(ip, 
11211);
client.addServer(ip, 
12003);
client.addServer(ip, 
12004);

  散列函数采用CRC32,你也可以采用其他散列函数,具体看场景测试而定,散列函数决定了你的查找节点效率和缓存重新分布的均衡程度。
 
  在完成1.0-beta这个里程碑版本后,xmemcached将集中于稳定性方面的测试和性能优化。欢迎提交测试报告和建议,我的email killme2008@gmail.com






评论

# re: xmemcached发布1.0-BETA版  回复  更多评论   

2009-03-09 18:10 by Arbow
哇噻,老庄打了鸡血般猛阿 :D

只有注册用户登录后才能发表评论。


网站导航: