Consistent hashing 一致性哈希算法以及Java实现(已做测试)

今天重新看了一致性hash算法,做了一下笔记,先保存下来吧,第一次手动实现了一遍,虽然有一部分是参考百度上的内容,但是也是根据bug自己改了一下

以下是网上参考以及自己理解的笔记:





以下代码参考网络:
package zlpTest;

import java.util.SortedMap;
import java.util.TreeMap;

//不带虚拟节点的一致性hash算法

public class ConsistentHashingWithouVirtualNode {
    
	//待加入hash环的服务器列表
	private static String Server[]={"192.168.0.112:1000","192.168.0.112:1001","192.168.0.112:1002","192.168.0.112:1003","192.168.0.112:1004"};
    //key表示服务器的hash值,value表示服务器
	private static SortedMap<Integer, String>sortedMap=new TreeMap<Integer, String>();
	//程序初始化,将服务器加到sortmap中
	static {
		for(int i =0;i<Server.length;i++){
			int hash = getHash(Server[i]);
			sortedMap.put(hash, Server[i]);
			System.out.println(Server[i]+"加入到集合中,其hash值为:"+hash);
		}
		
	}
	
	//得到应当路由到的节点
	private static String getServer(String key){
		//得到key的hash值
		int hash = getHash(key);
		
		//得到大于该hash值得所有的map
		SortedMap<Integer, String> subMap = sortedMap.tailMap(hash);
		if(subMap.isEmpty()){
			//如果没有比该key的hash值大的,则从第一个node开始
			Integer i = sortedMap.firstKey();
			//返回对应的服务器
			return sortedMap.get(i);
		}else{
			//第一个key就是顺时针过去离node最近的那个节点
			Integer i = subMap.firstKey();
			//返回对应的服务器
			return subMap.get(i);
		}
		
	}
	
	
	//计算服务器的hash值
	private static int getHash(String str) {
		final int p = 16777619;
		int hash=(int) 2166136261L;
		for(int i =0;i<str.length();i++){
			hash = (hash^str.charAt(i))*p;
			hash += hash <<13;
			hash ^= hash >>7;
			hash ^= hash >>17;
			hash += hash <<5;
		}
		//如果算出来的值为负数则取其绝对值
		if(hash<0){
			hash = Math.abs(hash);	
		}
		return hash;
	}
	
	public static void main(String args[]){
		String[] keys = {"太阳","月亮","星星"};
		for(int i = 0; i <keys.length;i++){
			System.out.println("["+keys[i]+"]的hash值为"+getHash(keys[i])+",被路由到节点["+getServer(keys [i])+"]");
			
		}
		
	}
}

结果如下:

192.168.0.112:1000加入到集合中,其hash值为:481136787
192.168.0.112:1001加入到集合中,其hash值为:1207862292
192.168.0.112:1002加入到集合中,其hash值为:970668381
192.168.0.112:1003加入到集合中,其hash值为:255327084
192.168.0.112:1004加入到集合中,其hash值为:2131030089
[太阳]的hash值为606807096,被路由到节点[192.168.0.112:1002]
[月亮]的hash值为244383856,被路由到节点[192.168.0.112:1003]
[星星]的hash值为1233020469,被路由到节点[192.168.0.112:1004]



package zlpTest;

import java.util.LinkedList;
import java.util.List;
import java.util.SortedMap;
import java.util.TreeMap;


以下代码实现中,网络参考上有bug,已经修改,可以正常运行
 public class ConsistentHashingWithVirtualNode {
    //待加入hash环的服务器列表
	private static String Server[]={"192.168.0.112:1000","192.168.0.112:1001","192.168.0.112:1002","192.168.0.112:1003","192.168.0.112:1004"};
    
	//真实节点列表,考虑到服务器上线、下线的场景,即添加、删除的场景会比较频繁,这里使用LinkedList会更好
	private static List<String> realNodes = new LinkedList<String>();
	
	//虚拟节点,key表示虚拟节点的hash值,value表示虚拟节点的名称
	private static SortedMap<Integer, String> virtualNodes = new TreeMap<Integer, String>();
	
	//虚拟节点的数目,这里写死,一个真实节点对应5个虚拟节点
	private static final int VIRTUAL_NODES = 5;
	 
	static{
		//先把原始的服务器添加到真实节点列表中
		for(int i = 0;i<Server.length;i++){
			realNodes.add(Server[i]);
		}
		//再添加虚拟节点,遍历linkedList使用foreach循环效率会比较高
		for(String str : realNodes){
			for(int i =0;i<VIRTUAL_NODES;i++){
				String virtualNodeName = str+"&&VN"+String.valueOf(i);
				int hash = getHash(virtualNodeName);
				System.out.println("虚拟节点["+virtualNodeName+"]被添加,hash值为"+hash);
				virtualNodes.put(hash, virtualNodeName);
			}
			
		}
		System.out.println();
	}

	 //使用FNV1_32_HASH算法计算服务器的Hash值,这里不使用重写hashCode的方法,最终效果没区别  
	private static int getHash(String str ) {
	         final int p = 16777619;  
	         int hash = (int)2166136261L;  
	         for (int i = 0; i < str.length(); i++){  
	             hash = (hash ^ str.charAt(i)) * p;  
	         }
	         hash += hash << 13;  
	         hash ^= hash >> 7;  
	         hash += hash << 3;  
	         hash ^= hash >> 17;  
	         hash += hash << 5;  
	           
	         // 如果算出来的值为负数则取其绝对值  
	         if (hash < 0)  {
	             hash = Math.abs(hash);  
	         }
	         return hash;    
	}
	
	//得到应当路由到的节点
	private static String getServer(String key){
		//得到该key的hash值
		int hash = getHash(key);
		//得到大于该hash值得所有map
		SortedMap<Integer, String> subMap = virtualNodes.tailMap(hash);
		String virtualNode;
		if(subMap.isEmpty()){
			//如果没有比该key的hash值大的,则从第一个node开始
			Integer i = virtualNodes.firstKey();
			//返回对应的服务器
			virtualNode = subMap.get(i);
		}else{
			//第一个key就是顺时针过去离node最近的那个节点
			Integer i = subMap.firstKey();
			//返回对应的服务器
			virtualNode = subMap.get(i);
		}
		//virtualNode虚拟节点名称要截取一下
		if(virtualNode.equals(null)){//可以修改为这种
			return virtualNode.substring(0,virtualNode.indexOf("&&"));
		}
		return virtualNode;// 这个地方之前返回的都是null,现在可以正常运行
	}
	 public static void main(String[] args){  
         String[] keys = {"太阳", "月亮", "星星"};  
         for(int i=0; i<keys.length; i++)  
             System.out.println("[" + keys[i] + "]的hash值为" +  
                     getHash(keys[i]) + ", 被路由到结点[" + getServer(keys[i]) + "]");  
     }  
}
运行结果:

虚拟节点[192.168.0.112:1000&&VN0]被添加,hash值为426605410
虚拟节点[192.168.0.112:1000&&VN1]被添加,hash值为806679074
虚拟节点[192.168.0.112:1000&&VN2]被添加,hash值为1854117913
虚拟节点[192.168.0.112:1000&&VN3]被添加,hash值为1685468505
虚拟节点[192.168.0.112:1000&&VN4]被添加,hash值为819484125
虚拟节点[192.168.0.112:1001&&VN0]被添加,hash值为1631497057
虚拟节点[192.168.0.112:1001&&VN1]被添加,hash值为919025337
虚拟节点[192.168.0.112:1001&&VN2]被添加,hash值为1323825146
虚拟节点[192.168.0.112:1001&&VN3]被添加,hash值为280852816
虚拟节点[192.168.0.112:1001&&VN4]被添加,hash值为648131592
虚拟节点[192.168.0.112:1002&&VN0]被添加,hash值为857453929
虚拟节点[192.168.0.112:1002&&VN1]被添加,hash值为239883060
虚拟节点[192.168.0.112:1002&&VN2]被添加,hash值为887503354
虚拟节点[192.168.0.112:1002&&VN3]被添加,hash值为548174262
虚拟节点[192.168.0.112:1002&&VN4]被添加,hash值为392490292
虚拟节点[192.168.0.112:1003&&VN0]被添加,hash值为96663676
虚拟节点[192.168.0.112:1003&&VN1]被添加,hash值为1227495797
虚拟节点[192.168.0.112:1003&&VN2]被添加,hash值为559011748
虚拟节点[192.168.0.112:1003&&VN3]被添加,hash值为296302209
虚拟节点[192.168.0.112:1003&&VN4]被添加,hash值为1211728920
虚拟节点[192.168.0.112:1004&&VN0]被添加,hash值为1881924403
虚拟节点[192.168.0.112:1004&&VN1]被添加,hash值为1991114675
虚拟节点[192.168.0.112:1004&&VN2]被添加,hash值为1652311339
虚拟节点[192.168.0.112:1004&&VN3]被添加,hash值为1096234241
虚拟节点[192.168.0.112:1004&&VN4]被添加,hash值为1405901184

[太阳]的hash值为1977106057, 被路由到结点[192.168.0.112:1004&&VN1]
[月亮]的hash值为1132637661, 被路由到结点[192.168.0.112:1003&&VN4]
[星星]的hash值为880019273, 被路由到结点[192.168.0.112:1002&&VN2]



参考网址:

http://blog.csdn.net/cywosp/article/details/23397179

http://blog.csdn.net/u010558660/article/details/52767218



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值