第五周作业

发布于: 2020 年 07 月 08 日

用你熟悉的编程语言实现一致性 hash 算法。

编写测试用例测试这个算法,测试 100 万 KV 数据,10 个服务器节点的情况下,计算这些 KV 数据在服务器上分布数量的标准差,以评估算法的存储负载不均衡性。

ServerNode.Java

public interface ServerNode {
// 获取节点
String GetNode(String key);
}

NodeList.Java

import java.util.LinkedList;
import java.util.List;
public class NodeList {
public NodeList(){
for (int i=0; i< Servers.length;i++){
Nodes.add(Servers[i]);
}
}
private static String[] Servers = {
"192.168.0.10:1230",
"192.168.1.10:1234",
"192.168.2.10:1123",
"192.168.3.10:1133",
"192.168.4.10:1223",
"192.168.5.10:1423",
"192.168.6.10:1523",
"192.168.7.10:1263",
"192.168.8.10:1238",
"192.168.9.10:1239",
};
// 虚拟节点数
public static final int VIRTUAL_NODES = 100;
private List<String> Nodes = new LinkedList<String>();
public List<String>GetNodes(){
return Nodes;
}
}

CoincidentHash.java

import org.springframework.util.StringUtils;
import java.util.SortedMap;
import java.util.TreeMap;
public class CoincidentHash implements ServerNode{
private CoincidentHash(){
for (String str : nodeList.GetNodes()){
for(int i=0; i<NodeList.VIRTUAL_NODES; i++){
String virtualNodeName = str + "&&VN" + i;
int hash = getHash(virtualNodeName);
System.out.println("虚拟节点[" + virtualNodeName + "]被添加, hash值为" + hash);
virtualNodes.put(hash, virtualNodeName);
}
}
}
// 真实节点
private NodeList nodeList = new NodeList();
// 虚拟节点,key表示虚拟节点的hash值,value表示虚拟节点的名称
private static SortedMap<Integer, String> virtualNodes = new TreeMap<Integer, String>();
@Override
public String GetNode(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 = virtualNodes.get(i);
}else{
//第一个Key就是顺时针过去离node最近的那个结点
Integer i = subMap.firstKey();
//返回对应的服务器
virtualNode = subMap.get(i);
}
//virtualNode虚拟节点名称要截取一下
if (!(StringUtils.isEmpty(virtualNode))){
return virtualNode.substring(0, virtualNode.indexOf("&&"));
}
return null;
}
private static class HashHolder{
private static CoincidentHash instance = new CoincidentHash();
}
public static CoincidentHash getInstance(){
return HashHolder.instance;
}
// 使用FNV1_32_HASH算法计算服务器的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 << 3;
hash ^= hash >> 17;
hash += hash << 5;
// 如果算出来的值为负数则取其绝对值
if (hash < 0)
hash = Math.abs(hash);
return hash;
}
}

Test.Java

import org.apache.commons.lang3.RandomStringUtils;
//计算标准差
public static double StandardDiviation(double[] x) {
int m=x.length;
double sum=0;
for(int i=0;i<m;i++){//求和
sum+=x[i];
}
double dAve=sum/m;//求平均值
double dVar=0;
for(int i=0;i<m;i++){//求方差
dVar+=(x[i]-dAve)*(x[i]-dAve);
}
//reture Math.sqrt(dVar/(m-1));
return Math.sqrt(dVar/m);
}
@Test
void testCoincidentHash(){
HashMap<String,Integer> serverArrangement = new HashMap<>();
// SortedMap<String,Integer> serverArrangement = new TreeMap<>();
NodeList nodeList = new NodeList();
for(String node : nodeList.GetNodes()) {
serverArrangement.put(node,0);
}
String key;
Integer count;
String keynode;
for (int i=0;i<1000000;i++){
key = RandomStringUtils.randomAlphanumeric(10);
keynode = CoincidentHash.getInstance().GetNode(key);
if (serverArrangement.containsKey(keynode)) {
count = serverArrangement.get(keynode) + 1;
serverArrangement.replace(keynode, count);
}
}
Integer[] counts = new Integer[serverArrangement.size()];
serverArrangement.values().toArray(counts);
double[] tmpCounts = new double[counts.length];
for (int i=0;i<counts.length;i++){
tmpCounts[i] = (double) counts[i];
}
double sd = StandardDiviation(tmpCounts);
System.out.println(String.format("10个节点,每个节点100个虚拟节点,100万个KV,分布标准差=%.2f",sd));
serverArrangement.forEach((k,v) -> System.out.println("server: "+ k+" keyNumber: "+v));
}

结果:

发布于: 2020 年 07 月 08 日 阅读数: 5
用户头像

王鑫龙

关注

还未添加个人签名 2018.02.04 加入

还未添加个人简介

评论

发布
暂无评论
第五周作业