架构师训练 第五周 作业
发布于: 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,数据量:100238IP:192.168.0.2,数据量:101367IP:192.168.0.3,数据量:100133IP:192.168.0.4,数据量:100085IP:192.168.0.5,数据量:99619IP:192.168.0.6,数据量:99967IP:192.168.0.7,数据量:98747IP:192.168.0.8,数据量:99758IP:192.168.0.9,数据量:100448IP:192.168.0.10,数据量:99638缓存在服务器上的标准差为:636.9315504824674
划线
评论
复制
发布于: 2020 年 07 月 08 日阅读数: 49
版权声明: 本文为 InfoQ 作者【李君】的原创文章。
原文链接:【http://xie.infoq.cn/article/baf52524712b12ad0cfc6a6a1】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。

李君
关注
结硬寨,打呆仗。 2018.09.11 加入
还未添加个人简介











 
    
评论