架构师训练营 第五周 作业
发布于: 2020 年 07 月 08 日
用你熟悉的编程语言实现一致性 hash 算法
public interface HashFunction {    //hash函数    Integer hash(String key);}
public class HashFunctionImpl implements HashFunction{    //FNV1_32_HASH算法    @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;    }}
import java.util.Collection;import java.util.SortedMap;import java.util.TreeMap;public class ConsistentHash {    private final HashFunction hashFunction;// hash 函数接口    private final int numberOfReplicas;// 每个机器节点关联的虚拟节点个数    private final SortedMap<Integer,Node> circle = new TreeMap<>();// 环形虚拟节点    /**     *     * @param hashFunction     *            hash 函数接口     * @param numberOfReplicas     *            每个机器节点关联的虚拟节点个数     * @param nodes     *            真实机器节点     */    public ConsistentHash(HashFunction hashFunction, int numberOfReplicas, Collection<Node> nodes) {        this.hashFunction = hashFunction;        this.numberOfReplicas = numberOfReplicas;        for (Node node : nodes) {            add(node);        }    }    /**     * 增加真实机器节点     *     * @param node     */    public void add(Node node) {        for (int i = 0; i < this.numberOfReplicas; i++) {            circle.put(this.hashFunction.hash(node.getIp() + i), node);        }    }    /**     * 删除真实机器节点     *     * @param node     */    public void remove(Node node) {        for (int i = 0; i < this.numberOfReplicas; i++) {            circle.remove(this.hashFunction.hash(node.getIp() + i));        }    }    /**     * 取得真实机器节点     *     * @param key     * @return     */    public Node get(String key) {        if (circle.isEmpty()) {            return null;        }        Integer hash = hashFunction.hash(key);        if (!circle.containsKey(hash)) {            SortedMap<Integer,Node> tailMap = circle.tailMap(hash);// 沿环的顺时针找到一个虚拟节点            hash = tailMap.isEmpty() ? circle.firstKey() : tailMap.firstKey();        }        return circle.get(hash); // 返回该虚拟节点对应的真实机器节点的信息    }}
// 物理机节点模拟类,保存节点的IP、名称、端口等信息public class Node {    private String ip;// IP    private String name;// 名称}
编写测试用例测试这个算法,测试 100 万 KV 数据,10 个服务器节点的情况下,计算这些 KV 数据在服务器上分布数量的标准差,以评估算法的存储负载不均衡性
public class HashTest {    private static final String IP_PREFIX = "192.168.1.";// 机器节点IP前缀    public static void main(String[] args) {        Map<String, Integer> map = new HashMap<String, Integer>();// 每台真实机器节点上保存的记录条数        HashFunction hashFunction = new HashFunctionImpl();        //真实物理节点        List<Node> realNodes = new ArrayList<>();        for (int i = 1; i <= 10; i++) {            map.put(IP_PREFIX + i, 0);// 每台真实机器节点上保存的记录条数初始为0            Node node = new Node(IP_PREFIX + i, "node" + i);            realNodes.add(node);        }        ConsistentHash consistentHash = new ConsistentHash(hashFunction,100,realNodes);        // 将10000条记录尽可能均匀的存储到10台机器节点        for (int i = 0; i < 10000; i++) {            // 产生随机一个字符串当做一条记录,可以是其它更复杂的业务对象,比如随机字符串相当于对象的业务唯一标识            String data = UUID.randomUUID().toString() + i;            // 通过记录找到真实机器节点            Node node = consistentHash.get(data);            // 这里可以通过其它工具将记录存储真实机器节点上,比如MemoryCache等            // ...            // 每台真实机器节点上保存的记录条数加1            map.put(node.getIp(), map.get(node.getIp()) + 1);        }        // 打印每台真实机器节点保存的记录条数        for (int i = 1; i <= 10; i++) {            System.out.println(IP_PREFIX + i + "节点记录条数:" + map.get("192.168.1." + i));        }        System.out.println("标准差为:"+StandardDiviation(map));    }    public static double StandardDiviation(Map<String, Integer> map) {        int m=map.size();        double sum=0;        for(int i=1;i<=m;i++){//求和            sum+=map.get("192.168.1."+i);        }        double dAve=sum/m;//求平均值        double dVar=0;        for(int i=1;i<=m;i++){//求方差            dVar+=(map.get("192.168.1."+i)-dAve)*(map.get("192.168.1."+i)-dAve);        }        return Math.sqrt(dVar/m);    }
192.168.1.1节点记录条数:882192.168.1.2节点记录条数:1051192.168.1.3节点记录条数:1217192.168.1.4节点记录条数:1120192.168.1.5节点记录条数:977192.168.1.6节点记录条数:1058192.168.1.7节点记录条数:1017192.168.1.8节点记录条数:952192.168.1.9节点记录条数:840192.168.1.10节点记录条数:886标准差为:110.94863676494633
 划线
   评论
  复制
发布于: 2020 年 07 月 08 日 阅读数: 29
亮灯
  关注 
还未添加个人签名 2018.02.14 加入
还未添加个人简介
 
 
  
  
 
 
 
 
 
 
 
 
 
 
    
评论