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

来源:互联网 发布:怎么更新windows系统 编辑:程序博客网 时间:2024/06/08 08:08

模拟了一个利用一致性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);//一共有四个serverConHashShard 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 mapTail 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;}@Overridepublic 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的一个典型的应用场景。



参考:

http://blog.csdn.net/haitao111313/article/details/7537799

0 0
原创粉丝点击