架构师训练 第五周 作业
发布于: 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 加入
还未添加个人简介
评论