第 5 周结构师训练营——作业
发布于: 2020 年 07 月 07 日
- 用你熟悉的编程语言实现一致性 hash 算法。 
- 编写测试用例测试这个算法,测试 100 万 KV 数据,10 个服务器节点的情况下,计算这些 KV 数据在服务器上分布数量的标准差,以评估算法的存储负载不均衡性。 
(1)不使用虚拟节点
public class ConsistentHashWithoutVirtualNode {	private static String[] servers = {"192.168.0.1:8090","192.168.0.2:8090","192.168.0.3:8090","192.168.0.4:8090",	"192.168.0.5:80990","192.168.0.6:8090","192.168.0.7:8090","192.168.0.8:8090","192.168.0.9:8090","192.168.0.10:8090"};		//key 表示服务器hash值,value表示服务器 	private static SortedMap<Integer,String> sortedMap = new TreeMap<Integer,String>();		//sortedMap表示闭型环,初始化将服务器加入到环中	static {		for (String server : servers) {			int hash = getHash(server);			sortedMap.put(hash, server);			System.out.println("服务器[" + server + "]加入集合中,对应的hash值为:" + hash);		}	}		/**	 * 获取数据存放的服务器的key	 * @param str	 */	private static Integer getserver(String str) {		int hash = getHash(str);		SortedMap<Integer,String> resultMap = sortedMap.tailMap(hash);		if(null == resultMap || resultMap.isEmpty()) 			return sortedMap.firstKey();		else 			return resultMap.firstKey();			}		/**	 * 获取key的hash值	 * @param key	 * @return	 */	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;	}		/**	 * 生成随机字符串	 * @param len	 * @return	 */	public static String generatedString(int len) {		byte[] array = new byte[len];		new Random().nextBytes(array);		String str = new String(array,Charset.forName("UTF-8"));		return str;	}		public static void main(String[] args) {		Map<Integer,Integer> countMap = new HashMap<Integer,Integer>();		for(int i=1;i<=1000000;i++) {			String str = generatedString(8);			Integer hash = getserver(str);			if(countMap.containsKey(hash))				countMap.put(hash, countMap.get(hash)+1);			else				countMap.put(hash, 1);		}		double sum = 0;		for(Integer hash : sortedMap.keySet()) {			sum += Math.pow(countMap.get(hash)-100000,2);			System.out.println("服务器["+sortedMap.get(hash)+"]存储数据量为:"+countMap.get(hash)+"占比为:"+(countMap.get(hash)/10000)+"%");		}		System.out.println("标准差为:"+(Math.sqrt(sum/10)));	}}输出结果

(2)使用虚拟节点
public class ConsistentHashUseVirtualNode {	//服务器列表	private static String[] servers = {"192.168.0.1:8090","192.168.0.2:8090","192.168.0.3:8090","192.168.0.4:8090",			"192.168.0.5:80990","192.168.0.6:8090","192.168.0.7:8090","192.168.0.8:8090","192.168.0.9:8090","192.168.0.10:8090"};		//便于生产上伸缩机器,使用LinkedList 存放服务器	private static List<String> realNode = new LinkedList<String>();		//虚拟节点,key表示hash value对应虚拟节点名称	private static SortedMap<Integer,String> virtualNode = new TreeMap<Integer,String>();		//设置虚拟节点数	private static final int vnode = 100;		static{		//将服务器加入到真实节点列表中		for(String str : servers) {			realNode.add(str);			//创建虚拟节点			for(int i = 1;i <= vnode ;i++) {				String serverName = str + "&&VN" + i;				int hash = getHash(serverName);				virtualNode.put(hash, serverName);			}		}			}		/**	 * 获取数据存储服务器	 * @param str	 * @return	 */	private static String getServer(String str) {		int hash = getHash(str);		String nodeName = null;		SortedMap<Integer,String> subMap = virtualNode.tailMap(hash);		if(null == subMap || subMap.isEmpty()) {			nodeName = virtualNode.get(virtualNode.firstKey());			return nodeName.substring(0, nodeName.indexOf("&&"));		}		nodeName = subMap.get(subMap.firstKey());		return nodeName.substring(0, nodeName.indexOf("&&"));		}		/**	 * 获取hash值	 * @param str	 * @return	 */	private static Integer 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;	}		/**	 * 生成随机字符串	 * @param len	 * @return	 */	public static String generatedString(int len) {		byte[] array = new byte[len];		new Random().nextBytes(array);		String str = new String(array,Charset.forName("UTF-8"));		return str;	}		public static void main(String[] args) {		System.out.println("虚拟节点为: "+vnode+" 的结果为:");		Map<String,Integer> resultMap = new  HashMap<String,Integer>();		double sum = 0;		for(int i = 1; i <= 1000000; i++) {			String str = generatedString(8);			String server = getServer(str);			if(resultMap.containsKey(server)) 				resultMap.put(server, resultMap.get(server)+1);			else				resultMap.put(server, 1);		}		for(String name : resultMap.keySet()) {			sum += Math.pow((resultMap.get(name)-100000), 2);			System.out.println("服务器[" + name + "]存储数据量为:" + resultMap.get(name)+"占比为:"+(resultMap.get(name)/10000)+"%");		}		System.out.println("标准差为:"+Math.sqrt(sum/10));	}}分别对虚拟节点数设为10,100,200,300,500场景做了验证,结果如下:
随着虚拟节点数的增加,数据分布率逐渐均匀,标准差逐渐减小;





划线
评论
复制
发布于: 2020 年 07 月 07 日阅读数: 48
jiangnanage
关注
还未添加个人签名 2019.04.11 加入
还未添加个人简介











 
    
评论