架构师训练营作业 (第五周)
发布于: 2020 年 07 月 06 日
作业一
- 用你熟悉的编程语言实现一致性hash算法。 
- 编写测试用例测试这个算法,测试 100 万 KV 数据,10 个服务器节点的情况下,计算这些 KV 数据在服务器上分布数量的标准差,以评估算法的存储负载不均衡性。 
0 添加依赖
计算hash采用了hutool工具包的方法,计算标准差使用了common-lang3工具中的方法
  <dependencies>  	<dependency>      <groupId>cn.hutool</groupId>      <artifactId>hutool-all</artifactId>      <version>5.3.7</version>    </dependency>    <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->    <dependency>      <groupId>org.apache.commons</groupId>      <artifactId>commons-lang3</artifactId>      <version>3.10</version>    </dependency>  </dependencies>
- 定义Hash算法接口 
/** * Hash 算法 */public interface HashAlgorithm {    /**     * 计算字符串key的哈希值     * @param key     * @return     */    public int getHash(String key);}- Hash算法的几种常见实现 
public class BernsteinHash implements HashAlgorithm{    @Override    public int getHash(String key) {        return HashUtil.bernstein(key);    }}public class DekHash implements HashAlgorithm{    @Override    public int getHash(String key) {        return HashUtil.dekHash(key);    }}public class FnvHash implements HashAlgorithm{    @Override    public int getHash(String key) {        return HashUtil.fnvHash(key);    }}public class JavaDefaultHash implements HashAlgorithm{    @Override    public int getHash(String key) {        return HashUtil.javaDefaultHash(key);    }}public class SdbmHash implements HashAlgorithm{    @Override    public int getHash(String key) {        return HashUtil.sdbmHash(key);    }}- 一致性hash类 
/** * 带虚拟节点的一致性Hash算法 */public class ConsistentHash {    private HashAlgorithm algorithm;    //待添加入Hash环的服务器列表    private String[] servers ;    //真实结点列表    private List<String> realNodes = new LinkedList<String>();    //虚拟节点,key表示虚拟节点的hash值,value表示虚拟节点的名称    private SortedMap<Integer, String> virtualNodes = new TreeMap<Integer, String>();    /**     * 虚拟节点数,每个服务器节点增加虚拟服务器的个数     */    private int virtualNum;    /**     * 各节点分布计数     */    private static Map<String, AtomicLong> countMap = new ConcurrentHashMap<String, AtomicLong>();    /**     *     * @param hashAlgorithm 指定Hash算法     * @param virtualNum 虚拟节点个数     */    public ConsistentHash(HashAlgorithm hashAlgorithm,int virtualNum,String [] serverList){        this.algorithm = hashAlgorithm;        this.virtualNum = virtualNum;        this.servers = serverList;        this.init();    }    /**     * 初始化哈希环     */    private void init() {        //先把原始的服务器添加到真实结点列表中        for (int i = 0; i < servers.length; i++){            realNodes.add(servers[i]);            countMap.put(servers[i],new AtomicLong(0));        }        //再添加虚拟节点        for (String str : realNodes) {            for (int i = 0; i < virtualNum; i++) {                String virtualNodeName = str + "#node" + String.valueOf(i);                int hash = algorithm.getHash(virtualNodeName);                virtualNodes.put(hash, virtualNodeName);            }        }    }    //得到应当路由到的结点    private String put(String key,String value) {        //得到该key的hash值        int hash = algorithm.getHash(key);        // 得到大于该Hash值的所有Map        SortedMap<Integer, String> subMap = this.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.isNotBlank(virtualNode)) {            String node = virtualNode.substring(0, virtualNode.indexOf("#node"));            AtomicLong atomicLong = countMap.get(node);            atomicLong.incrementAndGet();            countMap.put(node,atomicLong);            return node;        }        return null;    }    public StandardDeviationResult standardDeviation(){        for (int i = 0; i < 1000000; i++) {            put(RandomStringUtils.randomAlphanumeric(20),RandomStringUtils.randomAlphanumeric(6));        }        double[] values = new double[this.servers.length];        int index = 0;        for(Map.Entry<String,AtomicLong> entry : countMap.entrySet()){            values[index] = Double.valueOf(entry.getValue().get()).doubleValue();            index++;        }        double standardDeviation  = new StandardDeviation().evaluate(values);        double mean = new Mean().evaluate(values);        return new StandardDeviationResult(algorithm.getClass().getSimpleName(),mean,standardDeviation,this.servers.length,this.virtualNum);    }}
- 标准差计算结果类 
public class StandardDeviationResult {    /**     * 算法     */    private String algorithm;    /**     * 真实节点个数     */    private int realNodesNum;    /**     * 每个真实节点虚拟节点个数     */    private int virtualNum;    /**     * 平均值     */    private double mean;    /**     * 标准差     */    private double startDeviation;    public StandardDeviationResult(String algorithm,double mean, double startDeviation,int  realNodesNum,int virtualNum) {        this.algorithm = algorithm;        this.mean = mean;        this.startDeviation = startDeviation;        this.realNodesNum = realNodesNum;        this.virtualNum = virtualNum;    }    public double getMean() {        return mean;    }    public void setMean(double mean) {        this.mean = mean;    }    public double getStartDeviation() {        return startDeviation;    }    public void setStartDeviation(double startDeviation) {        this.startDeviation = startDeviation;    }    public String getAlgorithm() {        return algorithm;    }    public void setAlgorithm(String algorithm) {        this.algorithm = algorithm;    }    public int getRealNodesNum() {        return realNodesNum;    }    public void setRealNodesNum(int realNodesNum) {        this.realNodesNum = realNodesNum;    }    public int getVirtualNum() {        return virtualNum;    }    public void setVirtualNum(int virtualNum) {        this.virtualNum = virtualNum;    }    @Override    public String toString() {        return "StandardDeviationResult{" +                "algorithm='" + algorithm + '\'' +                ", realNodesNum=" + realNodesNum +                ", virtualNum=" + virtualNum +                ", mean=" + mean +                ", startDeviation=" + startDeviation +                '}';    }}
- 测试代码 
public class Starter {    public static void main(String[] args) throws Exception {        String [] servers = {                "192.168.31.101",                "192.168.31.102",                "192.168.31.103",                "192.168.31.104",                "192.168.31.105",                "192.168.31.106",                "192.168.31.107",                "192.168.31.108",                "192.168.31.109",                "192.168.31.110"        };        List<HashAlgorithm> hashAlgorithmList = new ArrayList<>();        hashAlgorithmList.add(new JavaDefaultHash());        hashAlgorithmList.add(new FnvHash());        hashAlgorithmList.add(new DekHash());        hashAlgorithmList.add(new BernsteinHash());        hashAlgorithmList.add(new SdbmHash());        for(HashAlgorithm hashAlgorithm : hashAlgorithmList){            ConsistentHash consistentHash = new ConsistentHash(hashAlgorithm,200,servers);            System.out.println(consistentHash.standardDeviation());        }    }}
- 测试结果 
StandardDeviationResult{algorithm='JavaDefaultHash', realNodesNum=10, virtualNum=200, mean=100000.0, startDeviation=45656.40662601471}StandardDeviationResult{algorithm='FnvHash', realNodesNum=10, virtualNum=200, mean=100000.0, startDeviation=9478.64732496737}StandardDeviationResult{algorithm='DekHash', realNodesNum=10, virtualNum=200, mean=100000.0, startDeviation=190343.8529935875}StandardDeviationResult{algorithm='BernsteinHash', realNodesNum=10, virtualNum=200, mean=100000.0, startDeviation=39476.91099307994}StandardDeviationResult{algorithm='SdbmHash', realNodesNum=10, virtualNum=200, mean=100000.0, startDeviation=43083.28226947328}
划线
评论
复制
发布于: 2020 年 07 月 06 日阅读数: 82
版权声明: 本文为 InfoQ 作者【王海】的原创文章。
原文链接:【http://xie.infoq.cn/article/f82534b6c1d464444d37880a9】。未经作者许可,禁止转载。

王海
关注
还未添加个人签名 2018.06.17 加入
还未添加个人简介











 
    
评论