您的位置:首页 > 其它

模拟客户端分布式的一致性hash

2014-10-20 20:02 232 查看
模拟了一个利用一致性hash的客户端分布式缓存的应用场景,实现了put操作,其实get操作也是一样:

<span style="font-size:18px;">public class ConHashShard {

private TreeMap<Long, Server> servers; // 增加虚拟节点后的hash值和server hash值的映射
private final int VIRTUAL_NOde = 100; // 每个server节点的虚拟节点个数

public ConHashShard(List<Server> serverlist) {
init(serverlist);
}

/**
* 这里可以把server当做缓存服务器
* @param args
*/
public static void main(String[] args) {
Server s1 = new Server("server1", "192.168.1.1:80");
Server s2 = new Server("server2", "192.168.1.2:80");
Server s3 = new Server("server3", "192.168.1.3:80");
Server s4 = new Server("server4", "192.168.1.4:80");
List<Server> servers = new ArrayList<Server>(); // 真实机器节点
servers.add(s1);
servers.add(s2);
servers.add(s3);
servers.add(s4); //一共有四个server
ConHashShard sh = new ConHashShard(servers);
System.out.println("sharding 成功,总共有4个server,每个server有100个虚拟节点");

//通过一致性hash算法,像server发送数据
sh.put("dong11","value1");
sh.put("东","value2");
sh.put("key33","value3");
sh.put("key44","value4");
sh.put("key55","value5");
sh.put("key66","value6");
sh.put("key77","value7");
sh.put("key88","value8");

//模拟有一个server down了
sh.delServer(s1);
System.out.println("删除server1");
sh.put("key55","value5");
sh.put("key66","value6");
sh.put("key77","value7");

//server 重新启动
sh.addServer(s1);
System.out.println("增加server1: ");

sh.put("key55","value5");
sh.put("key66","value6");
sh.put("key77","value7");

}

private void delServer(Server server) {
for (int n = 0; n < VIRTUAL_NOde; n++) {
servers.remove(hash("SHARD-" + server.getName() + "-NODE-" + n));
}
}

private void init(List<Server> serverlist) { // 初始化一致性hash环
servers = new TreeMap<Long, Server>();
for (Server server : serverlist) { // 每个真实机器节点都需要关联虚拟节点
for (int n = 0; n < VIRTUAL_NOde; n++)
//hash("SHARD-" + shardInfo.name + "-NODE-" + n)把不同的hash尽量均匀
servers.put(hash("SHARD-" + server.getName() + "-NODE-" + n), server);
}
}

private void addServer(Server s) {
for (int n = 0; n < VIRTUAL_NOde; n++)
servers.put(hash("SHARD-" + s.getName() + "-NODE-" + n), s);

}

public void put(String key,String value) {
//方法用于返回此映射,其键大于或等于fromKey的部分视图。
//返回的映射受此映射支持,因此改变返回映射反映在此映射中,反之亦然。
/**
* http://www.yiibai.com/java/util/treemap_tailmap.html *
* public class TreeMapDemo {
public static void main(String[] args) {
TreeMap<Integer, String> treemap = new TreeMap<Integer, String>();
SortedMap<Integer, String> treemapincl = new TreeMap<Integer, String>();

` treemap.put(2, "two");
treemap.put(1, "one");
treemap.put(3, "three");
treemap.put(6, "six");
treemap.put(5, "five");

System.out.println("Getting tail map");
treemapincl=treemap.tailMap(3);
System.out.println("Tail map values: "+treemapincl);
}
}
上面的代码执行入下:
Getting tail map
Tail map values: {3=three, 5=five, 6=six}
*/
SortedMap<Long, Server> tail = servers.tailMap(hash(key)); // 沿环的顺时针找到一个虚拟节点
if (tail.size() == 0) {
return;
}
Server server = tail.get(tail.firstKey());
server.put(key, value);
System.out.println("把key: " + key + " vlaue: " + value + " 存入了: " + tail.get(tail.firstKey()));
System.out.println("数据的hash值为: " + hash(key) + " server的hash值为: " + tail.firstKey());
}

/**
* MurMurHash算法,是非加密HASH算法,性能很高,
* 比传统的CRC32,MD5,SHA-1(这两个算法都是加密HASH算法,复杂度本身就很高,带来的性能上的损害也不可避免)
* 等HASH算法要快很多,而且据说这个算法的碰撞率很低. http://murmurhash.googlepages.com/ */
private static Long hash(String key) {

ByteBuffer buf = ByteBuffer.wrap(key.getBytes());
int seed = 0x1234ABCD;

ByteOrder byteOrder = buf.order();
buf.order(ByteOrder.LITTLE_ENDIAN);

long m = 0xc6a4a7935bd1e995L;
int r = 47;

long h = seed ^ (buf.remaining() * m);

long k;
while (buf.remaining() >= 8) {
k = buf.getLong();

k *= m;
k ^= k >>> r;
k *= m;

h ^= k;
h *= m;
}

if (buf.remaining() > 0) {
ByteBuffer finish = ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN);
// for big-endian version, do this first:
// finish.position(8-buf.remaining());
finish.put(buf).rewind();
h ^= finish.getLong();
h *= m;
}

h ^= h >>> r;
h *= m;
h ^= h >>> r;

buf.order(byteOrder);
return h;
}
}</span>
<span style="font-size:18px;">package com.dong.istudy.consistenthash;

import java.util.HashMap;
import java.util.Map;

</span>
<span style="font-size:18px;">//缓存服务器</span>
<span style="font-size:18px;">public class Server {

private String name;
private String ip;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getIp() {
return ip;
}

public void setIp(String ip) {
this.ip = ip;
}

private Map<String,String> map = new HashMap<String,String>();

public Server(String name, String ip) {
this.name = name;
this.ip = ip;
}

@Override
public String toString() {
return name + "-" + ip;
}

public void put(String key,String value) {
map.put(key, value);
}

public String get(String key) {
return map.get(key);
}
}
</span>


上面的程序模拟了利用客户端进行分布式的缓存方案。但是有一个问题,如果客户端也是分布式的怎么办,也就是说如果有一个缓存server down掉了,如何确保每一个客户端都得到了通知?
解决方案如下:

1,把用到了所有的缓存server的配置信息存储到消息队列比如Activemq,客户端采用发布订阅的模式监听server配置信息的变化;

2,解决方案一看似解决了问题,但是Activemq这个时候也形成了单点怎么办?可以把Activemq做成分布式;

3,那么如果保证Activemq的中配置信息数据的强一致性呢?比如说,有缓存server down掉,或者是主动的增加,移除一个缓存server 所造成的数据一致性的问题。因此这个时候可以采用zookeeper,zookeeper正可以保证强一致性和高可用性(zookeeper集群),而存储配置信息也正是zookeeper的
4000
一个典型的应用场景。

参考:
http://blog.csdn.net/haitao111313/article/details/7537799
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: