写点什么

架构师训练 第五周 作业

用户头像
李君
关注
发布于: 2020 年 07 月 08 日
架构师训练 第五周 作业

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

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

1. 一致性hash 算法实现类

/**
* ConsistencyHash
*
* @author rikun
*/
public class ConsistencyHash {
/**
* 服务器列表
*/
protected List<Server> nodes;
/**
* 虚拟节点和服务器的映射关系
*/
private SortedMap<Long, Server> virNodes = new TreeMap<>();
/**
* 虚拟节点数
*/
private int VIR_NODE_COUNT = 0;
public ConsistencyHash(int virNodeCount) {
this.nodes = new ArrayList<>();
this.VIR_NODE_COUNT = virNodeCount;
}
/**
* 添加服务器
*
* @param node
* @param virNode
*/
@Override
public void addNode(Server server, Integer virNode) {
this.nodes.add(server);
VIR_NODE_COUNT += virNode;
virNodes = new TreeMap<>();
IntStream.range(0, VIR_NODE_COUNT)
.forEach(index -> {
nodes.forEach(node -> {
long hash = getKetAmaHash(node.getIp() + index);
virNodes.put(hash, node);
});
});
}
/**
* 删除服务器
*
* @param node
* @param virNode
*/
public void removeNode(Server server, Integer virNode) {
nodes.removeIf(item -> server.getIp().equals(item.getIp()));
VIR_NODE_COUNT -= virNode;
virNodes = new TreeMap<>();
IntStream.range(0, VIR_NODE_COUNT)
.forEach(index -> {
nodes.forEach(node -> {
long hash = getKetAmaHash(node.getIp() + index);
virNodes.put(hash, node);
});
});
}
/**
* 取出服务器节点
*
* @param key
* @return
*/
public Server get(String key) {
long hash = getKetAmaHash(key);
SortedMap<Long, Server> subMap = hash >= virNodes.lastKey() ? virNodes.tailMap(0L) : virNodes.tailMap(hash);
if (subMap.isEmpty()) {
return null;
}
return subMap.get(subMap.firstKey());
}
/**
* KETAMA_HASH
* @param key
*
* @return
*/
public Long getKetAmaHash(String key) {
MessageDigest md5 = null;
try {
md5 = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
System.out.print("++++ no md5 algorythm found");
throw new IllegalStateException("++++ no md5 algorythm found");
}
md5.reset();
md5.update(key.getBytes());
byte[] bKey = md5.digest();
long res = ((long) (bKey[3] & 0xFF) << 24)
| ((long) (bKey[2] & 0xFF) << 16)
| ((long) (bKey[1] & 0xFF) << 8) | (long) (bKey[0] & 0xFF);
return res;
}
}



2. 服务器缓存类

/**
* Server
*
* @author Lee
*/
public class Server {
/**
* 服务器 IP
*/
private String ip;
/**
* 缓存集
*/
private Map<String, Object> data;
public Server(String ip) {
this.ip = ip;
data = new HashMap<>();
}
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public <T> void put(String key, T value) {
data.put(key, value);
}
public void remove(String key){
data.remove(key);
}
public <T> T get(String key) {
return (T) data.get(key);
}
public int getSize() {
return data.size();
}
}



3. 测试case

/**
* TestCase
*
* @author rikun
*/
public class TestCase {
public static void main(String[] args) {
// 10个服务器节点,2000个虚拟节点
ConsistencyHash cluster = new ConsistencyHash(2000);
int nodes = 340;
cluster.addNode(new Server("192.168.0.1"), nodes);
cluster.addNode(new Server("192.168.0.2"), nodes);
cluster.addNode(new Server("192.168.0.3"), nodes);
cluster.addNode(new Server("192.168.0.4"), nodes);
cluster.addNode(new Server("192.168.0.5"), nodes);
cluster.addNode(new Server("192.168.0.6"), nodes);
cluster.addNode(new Server("192.168.0.7"), nodes);
cluster.addNode(new Server("192.168.0.8"), nodes);
cluster.addNode(new Server("192.168.0.9"), nodes);
cluster.addNode(new Server("192.168.0.10"), nodes);
// 设置100万个缓存到服务器节点
int dataCount = 1000000;
String pre_key = "ch";
IntStream.range(0, dataCount)
.forEach(index -> {
Server node = cluster.get(pre_key + index);
node.put(pre_key + index, "Test Data");
});
System.out.println("数据分布情况:");
// 服务器上的缓存数量
List<Integer> numberOfDivisions = new ArrayList<>();
cluster.nodes.forEach(node -> {
System.out.println("IP:" + node.getIp() + ",数据量:" + node.getSize());
numberOfDivisions.add(node.getSize());
});
System.out.println("缓存在服务器上的标准差为:" +
standardDeviation(numberOfDivisions.toArray(new Integer[numberOfDivisions.size()])));
}
/**
* 求标准差
*
* @param divisions
* @return
*/
public static double standardDeviation(Integer[] divisions) {
int totals = divisions.length;
double sum = 0D;
// 求和
for (Integer item: divisions) {
sum += item;
}
// 求平均值
double avg = sum/totals;
// 求方差
double variance = 0;
for (Integer item: divisions) {
variance += (item - avg) * (item - avg);
}
return Math.sqrt(variance/totals);
}
}



4. 结果

数据分布情况:
IP:192.168.0.1,数据量:100238
IP:192.168.0.2,数据量:101367
IP:192.168.0.3,数据量:100133
IP:192.168.0.4,数据量:100085
IP:192.168.0.5,数据量:99619
IP:192.168.0.6,数据量:99967
IP:192.168.0.7,数据量:98747
IP:192.168.0.8,数据量:99758
IP:192.168.0.9,数据量:100448
IP:192.168.0.10,数据量:99638
缓存在服务器上的标准差为:636.9315504824674



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

李君

关注

结硬寨,打呆仗。 2018.09.11 加入

还未添加个人简介

评论

发布
暂无评论
架构师训练 第五周 作业