之前的文章当中,我们学习了一致性哈希算发的原理。今天我们用java来实现一下,一致性哈希算法,重点还是一致性和虚拟节点2个方面。
废话少说,直接上代码:
public class ConsistencyHash {
private TreeMap<Long, String> nodes = null; private List<String> shards = new ArrayList<String>(); private int VIRTUAL_NUM = 4;public void init() { shards.add("10.10.0.0-服务器0"); shards.add("10.10.0.1-服务器1"); shards.add("10.10.0.2-服务器2"); shards.add("10.10.0.3-服务器3"); shards.add("10.10.0.4-服务器4");
nodes = new TreeMap<Long, String>();
for (int i = 0; i < shards.size(); i++) { String shardInfo = shards.get(i); for (int j = 0; j < VIRTUAL_NUM; j++) { byte[] md5Value = computeMd5("SHARD-" + i + "-NODE-" + j); long hashCode = hash(md5Value, j); System.out.println(hashCode); nodes.put(hashCode, shardInfo); } } Iterator<Long> iter = nodes.keySet().iterator(); while(iter.hasNext()){ Long key = iter.next(); String obj = (String)nodes.get(key); System.out.println(String.valueOf(key) + ":" + obj); } }public Object getShardInfo(long hash) { Long key = hash; SortedMap<Long, String> tailMap = nodes.tailMap(key); if (tailMap.isEmpty()) { key = nodes.firstKey(); } else { key = tailMap.firstKey(); } return nodes.get(key); }
public void printMap() { System.out.println(nodes); }
public long hash(byte[] digest, int nTime) { long rv = ((long) (digest[3 + nTime * 4] & 0xFF) << 24) | ((long) (digest[2 + nTime * 4] & 0xFF) << 16) | ((long) (digest[1 + nTime * 4] & 0xFF) << 8) | (digest[0 + nTime * 4] & 0xFF);
return rv & 0xffffffffL; /* Truncate to 32-bits */
}/**
* Get the md5 of the given key. 计算MD5值 */ public byte[] computeMd5(String k) { MessageDigest md5; try { md5 = MessageDigest.getInstance("MD5"); } catch (NoSuchAlgorithmException e) { throw new RuntimeException("MD5 not supported", e); } md5.reset(); byte[] keyBytes = null; try { keyBytes = k.getBytes("UTF-8"); } catch (UnsupportedEncodingException e) { throw new RuntimeException("Unknown string :" + k, e); }md5.update(keyBytes);
byte[] res = md5.digest(); return res; }public static void main(String[] args) {
Random ran = new Random(); ConsistencyHash hash = new ConsistencyHash(); hash.init(); hash.printMap();for (int i = 0; i < 256; i++) {
int randomVirtualServer = ran.nextInt(hash.VIRTUAL_NUM); byte[] md5Value = hash.computeMd5(String.valueOf(i)); long hashValue = hash.hash(md5Value, randomVirtualServer); String serverInfo = (String)hash.getShardInfo(hashValue); System.out.println(serverInfo); } } }环境:假定为5台真实服务器,每台真实服务器对应4个虚拟节点,这样就是20个服务节点。
首先是init()方法,该方法就是把20个服务节点,分布在一个2^32的圆环上。这里分为2步,首先根据形如“ SHARD-2-NODE-3 ”这样的字符串得到一个16个长度的byte数组;之后根据这个byte数组获得2^32范围内的哈希值。
然后假定256个数据对象,我们需要为他们分别找到一个服务节点。
根据一致性的定义,我们需要根据这256个数据对象,分别一个 2^32范围内的哈希值 ,为了方便起见,我们采取和服务节点一致的方法。
之后,在 getShardInfo 方法当中,在圆环内,为他们寻找各自所属的服务节点。