一致性 Hash 算法实现 - Java
发布于: 2020 年 07 月 08 日
Hash环的实现
面向接口编程,定义环需要的接口
public interface ConsistentHashing { // 支持虚拟节点 public void addNode(String key, HashNode node) throws UnsupportedEncodingException, NoSuchAlgorithmException; // 环需要有序 public void sort(); // 取最近的节点 public HashNode getFirstNode(String key) throws UnsupportedEncodingException, NoSuchAlgorithmException;}
节点类
public class HashNode { String val; public HashNode(String val) { this.val = val; } public String getVal() { return val; }}
TreeMap实现环
public class HashTreeMap implements ConsistentHashing{ TreeMap<Long, HashNode> treeMap = new TreeMap<>(); ConsistentHashingAlg hashingAlg; public HashTreeMap(String alg) { this.hashingAlg = ConsistentHashingAlgFactory.getSignelStrategy(alg); } // 一个主机节点映射出的虚拟节点数量 private static final int VIRTUAL_NODE_SIZE = 10; @Override public void addNode(String key, HashNode node) throws UnsupportedEncodingException, NoSuchAlgorithmException { // 加节点自己 treeMap.put(hashingAlg.hash(key), node); // 虚拟节点 对应同一个node for (int i = 0; i < VIRTUAL_NODE_SIZE; i++) { long hashCode = hashingAlg.hash(key + String.valueOf(i)); treeMap.put(hashCode, node); } } // TreeMap本身有序 @Override public void sort() { } @Override public HashNode getFirstNode(String key) throws UnsupportedEncodingException, NoSuchAlgorithmException { long hashCode = hashingAlg.hash(key); // 取大于参数值的子集 SortedMap<Long, HashNode> res = treeMap.tailMap(hashCode); if (res.isEmpty()) { return treeMap.firstEntry().getValue(); } return res.get(res.firstKey()); }
2.Hash算法
有多种实现方式,所以采用策略模式
// 抽象策略public interface ConsistentHashingAlg { public long hash(String value) throws NoSuchAlgorithmException, UnsupportedEncodingException;}
// 具体策略public class HashingMD5 implements ConsistentHashingAlg{ @Override public long hash(String value) throws NoSuchAlgorithmException, UnsupportedEncodingException { MessageDigest md5 = MessageDigest.getInstance("MD5"); md5.reset(); byte[] keyBytes = value.getBytes("UTF-8"); md5.update(keyBytes); byte[] digest = md5.digest(); // hash code long md5Code = ((long) (digest[3] & 0xFF) << 24) | ((long) (digest[2] & 0xFF) << 16) | ((long) (digest[1] & 0xFF) << 8) | (digest[0] & 0xFF); long hashCode = md5Code & 0xFFFFFFFFL; return hashCode; }}
// 策略工厂public class ConsistentHashingAlgFactory { private static final HashMap<String, ConsistentHashingAlg> strategys = new HashMap<>(); static { strategys.put("MD5", new HashingMD5()); } public static ConsistentHashingAlg getSignelStrategy(String alg) { return strategys.get(alg); }}
3.测试Demo
static String[] ips = {"192.168.2.9", "119.45.56.8", "45.113.8.40", "192.168.10.0", "47.123.8.8", "36.98.233.54", "142.9.0.98", "119.9.72.90", "69.34.3.87", "123.48.9.9"}; public static void main(String[] args) throws UnsupportedEncodingException, NoSuchAlgorithmException { getStandardDeviation(); } // KV 数据在10个服务器上分布数量的标准差 // Standard Deviation =(每个服务器上有多少个映射 - 平均值100万除以10)* 2 private static void getStandardDeviation() throws UnsupportedEncodingException, NoSuchAlgorithmException { double avgVal = 1000000 / 10; HashTreeMap hashTreeMapV = new HashTreeMap("MD5"); // 存储node的映射数量 HashMap<String, Integer> nodeMap = new HashMap<>(); // 创建10个服务器 for (int i = 0; i < ips.length; i++) { hashTreeMapV.addNode(ips[i], new HashNode(ips[i])); nodeMap.put(ips[i], 0); } // 测试 100 万 KV 数据 key = i for (int i = 0; i < 1000000; i++) { String ip = hashTreeMapV.getFirstNode(String.valueOf(i)).getVal(); nodeMap.put(ip, nodeMap.get(ip) + 1); } nodeMap.forEach((key, value) -> System.out.println(key + " 路由数量:" + value)); // 计算方差 double variance = 0; for (double x : nodeMap.values()) { variance += Math.pow((x - avgVal), 2); } variance = variance / ips.length; System.out.println("方差:" + new BigDecimal(variance)); // 计算标准差 double standardDeviation = Math.sqrt(variance); System.out.println("标准差" + standardDeviation); }
测试结果
// 10个节点 * 10个虚拟节点142.9.0.98 路由数量:7977145.113.8.40 路由数量:8328336.98.233.54 路由数量:9513369.34.3.87 路由数量:10391247.123.8.8 路由数量:128768119.9.72.90 路由数量:102948192.168.10.0 路由数量:102002192.168.2.9 路由数量:150841123.48.9.9 路由数量:72650119.45.56.8 路由数量:80692方差:527358714标准差22964.292151076635
划线
评论
复制
发布于: 2020 年 07 月 08 日 阅读数: 48
羽球
关注
还未添加个人签名 2017.10.28 加入
还未添加个人简介
评论