模拟客户端分布式的一致性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>
上面的程序模拟了利用客户端进行分布式的缓存方案。但是有一个问题,如果客户端也是分布式的怎么办,也就是说如果有一个缓存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
<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
相关文章推荐
- Redis分布式部署,一致性hash
- 分布式与集群的区别,一致性hash,hadoop与HBASE,消息,关于分布式系统的数据一致性问题(来自公众号:360doc个人图书馆)
- 一致性 Hash 算法学习(分布式或均衡算法)
- 一致性hash在分布式系统中的应用
- jedis分布式之 ShardedJedisPool (一致性Hash分片算法)
- 分布式中一致性Hash的应用
- 分布式缓存中的一致性Hash 算法
- Redis分布式部署,一致性hash;分布式与缓存队列
- 分布式系统中的算法设计(一) -- 一致性 Hash
- java实现分布式环境Hash一致性
- 一致性 Hash 算法学习(分布式或均衡算法)
- 一致性 Hash 算法学习(分布式或均衡算法)
- 分布式缓存的一致性Hash的Java实现
- libketama consistent hash (一致性hash) memcache 客户端
- Consul实现原理系列文章1: 用Raft来实现分布式一致性
- 分布式之数据库和缓存双写一致性方案解析(二)
- 一致性hash以及python代码实现
- 分布式系统一致性方案
- [转帖]一致性Hash
- 探索c#之一致性Hash详解