架构师训练营 - 第五周作业
发布于: 2020 年 10 月 25 日
用你熟悉的编程语言实现一致性 hash 算法。
编写测试用例测试这个算法,测试 100 万 KV 数据,10 个服务器节点的情况下,计算这些 KV 数据在服务器上分布数量的标准差,以评估算法的存储负载不均衡性。
代码实现
@FunctionalInterfacepublic interface HashFunction { Integer hash(String key);}
public class Fnv1Function implements HashFunction { @Override public Integer hash(String key) { final int p = 16777619; int hash = (int) 2166136261L; for (int i = 0; i < key.length(); i++) hash = (hash ^ key.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; }}
@AllArgsConstructor@Getter@Setterpublic class Node { private String ip; private String name;}
public class ConsistentHash { private HashFunction hashFunction; private int numberOfReplicas; private SortedMap<Integer, Node> circle = new TreeMap<>(); public ConsistentHash(HashFunction hashFunction, int numberOfReplicas, Collection<Node> nodes) { this.hashFunction = hashFunction; this.numberOfReplicas = numberOfReplicas; for (Node node : nodes) { add(node); } } public void add(Node node) { for (int i = 0; i < numberOfReplicas; i++) { circle.put(this.hashFunction.hash(getKey(node, i)), node); } } public void remove(Node node) { for (int i = 0; i < numberOfReplicas; i++) { circle.remove(getKey(node, i)); } } private String getKey(Node node, int virtualNode) { StringBuilder builder = new StringBuilder(node.getIp()) .append("&&") .append("VIRTUAL_NODE=") .append(virtualNode); return builder.toString(); } public Node get(String key) { if (circle.isEmpty()) { return null; } Integer hashCode = hashFunction.hash(key); if (!circle.containsKey(hashCode)) { SortedMap<Integer, Node> tailMap = circle.tailMap(hashCode); hashCode = tailMap.isEmpty() ? circle.firstKey() : tailMap.firstKey(); } return circle.get(hashCode); }}
public class ConsistentHashTest { private static final String IP_PREFIX = "127.0.0."; public static void main(String[] args) { Map<String, Integer> map = new HashMap<>();// 每台真实机器节点上保存的记录条数 List<Node> realNodes = new ArrayList<>(); for (int i = 1; i < 11; i++) { String ip = IP_PREFIX + i; String nodeName = "NODE_" + i; map.put(ip, 0); realNodes.add(new Node(ip, nodeName)); } ConsistentHash consistentHash = new ConsistentHash(new Fnv1Function(), 100, realNodes); // 将1000000条记录存储到10台机器节点 for (int i = 0; i < 1000000; i++) { String data = UUID.randomUUID().toString() + i; Node node = consistentHash.get(data); //do something map.put(node.getIp(), map.get(node.getIp()) + 1); } for (Map.Entry<String, Integer> entry : map.entrySet()) { System.out.println("IP为:" + entry.getKey() + "; 总条数为:" + entry.getValue()); } Collection<Integer> values = map.values(); Integer[] array = values.toArray(new Integer[values.size()]); System.out.println("标准差为:" + calculateStandardDeviation(array)); } public static double calculateStandardDeviation(Integer[] arr) { int m = arr.length; double sum = 0; for (int i = 0; i < m; i++) {//求和 sum += arr[i]; } double dAve = sum / m;//求平均值 double dVar = 0; for (int i = 0; i < m; i++) {//求方差 dVar += (arr[i] - dAve) * (arr[i] - dAve); } //reture Math.sqrt(dVar/(m-1)); return Math.sqrt(dVar / m); }}
计算结果
IP为:192.168.0.2; 总条数为:96433IP为:192.168.0.1; 总条数为:120307IP为:192.168.0.4; 总条数为:85154IP为:192.168.0.3; 总条数为:105413IP为:192.168.0.10; 总条数为:94731IP为:192.168.0.9; 总条数为:106091IP为:192.168.0.6; 总条数为:96966IP为:192.168.0.5; 总条数为:83885IP为:192.168.0.8; 总条数为:105772IP为:192.168.0.7; 总条数为:105248标准差为:10341.27909883492
划线
评论
复制
发布于: 2020 年 10 月 25 日阅读数: 25
版权声明: 本文为 InfoQ 作者【chenlovehx】的原创文章。
原文链接:【http://xie.infoq.cn/article/2b40c39e0d1533bc3e5b12eda】。文章转载请联系作者。
chenlovehx
关注
还未添加个人签名 2018.04.26 加入
还未添加个人简介
评论