写点什么

Week 5 命题作业

用户头像
balsamspear
关注
发布于: 2020 年 11 月 06 日

实现思路:

  1. 使用支持排序的 TreeMap 实现 Hash 环

  2. 使用 Java 原生的 Object.hashCode 获取 hashCode

  3. 每台服务器使用 50 个节点,三台服务器共 150 个节点(经验值?)



public class ConsistentHashUtils {
/**
* 服务器节点,应该从配置文件中取读
*/
private static String[] servers = {"192.12.43.222", "44.36.14.25", "45.26.48.91"};
/**
* 使用排序集合 TreeMap 实现 Hash 环
*/
private static SortedMap<Long, String> hashRing = new TreeMap<>();
/**
* 生成 Hash 环,并安放服务器节点及其虚拟节点。
* <p>
* 1. 当服务器节点发生改变(增删改)时,需要重新生成
* 2. 服务器节点不变时,Hash 环也不能变,否则可能一个 key 每次分配到的服务器不一样
* 3. 这个方法应该是在程序启动时执行,且在服务器节点发生改变时,由专门的后台线程执行!
*/
public static void generateHashRing() {
for (String server : servers) {
// 把服务器及对应虚拟节点保存到 treeMap 中,也就是放到 hash 环上
// 这里每个服务器写死 49 个虚拟节点
for (int i = 0; i < 50; i++) {
// 加上一个随机值,保证所有虚拟节点计算的 hash 值不同
long hashCode = getHashCode(server + UUID.randomUUID());
// 上环
hashRing.put(hashCode, server);
}
}
}
/**
* 获取一致性 Hash 算法分配到的服务器节点
*
* treeMap 按自然数从小到大排序,要找出第一个比 dataKey HashCode 大的服务器节点,也就是顺时针第一个,
* 直接将 treeMap 从 dataKey HashCode 截断即可,然后有两种情况
* 1. 没有比它大的,则取整个 treeMap 的第一条数据
* 2. 否则,取截断后的第一条数据
*
* @param dataKey key
* @return 服务器节点
*/
public static String getServerByConsistentHash(String dataKey) {
String server;
SortedMap<Long, String> tailMap = hashRing.tailMap(getHashCode(dataKey));
if (tailMap.isEmpty()) {
server = hashRing.get(hashRing.firstKey());
} else {
server = hashRing.get(tailMap.firstKey());
}
return server;
}
/**
* 获取字符串对应的 hashCode
*
* @param str 需要计算 Hash 的字符串
* @return hashCode
*/
private static long getHashCode(String str) {
return Math.abs(str.hashCode()) << 2;
}
/**
* 测试
*/
public static void main(String[] args) {
// 使用 Map 记录测试结果
Map<String, Integer> resultMap = new HashMap<>();
for (String server : servers) {
resultMap.put(server, 0);
}
// 生成 Hash 环
generateHashRing();
// 测试 10 万条数据
for (int i = 0; i < 100000; i++) {
String key = UUID.randomUUID().toString();
String server = getServerByConsistentHash(key);
resultMap.put(server, resultMap.get(server) + 1);
}
resultMap.forEach((key, value) -> System.out.println(String.format("服务器 %s,分配到的数量:%d", key, value)));
}
}

测试结果:

服务器 44.36.14.25,分配到的数量:30640
服务器 45.26.48.91,分配到的数量:35955
服务器 192.12.43.222,分配到的数量:33405



用户头像

balsamspear

关注

还未添加个人签名 2019.10.24 加入

还未添加个人简介

评论

发布
暂无评论
Week 5命题作业