├── ConsistentHashRouter.java ├── PhysicalNode.java ├── README.md └── VirtualNode.java /ConsistentHashRouter.java: -------------------------------------------------------------------------------- 1 | package com.moe.oa.util.hash; 2 | 3 | import java.security.MessageDigest; 4 | import java.security.NoSuchAlgorithmException; 5 | import java.util.Collection; 6 | import java.util.Iterator; 7 | import java.util.SortedMap; 8 | import java.util.TreeMap; 9 | 10 | import com.moe.oa.model.VirtualNode; 11 | import com.moe.oa.model.PhysicalNode; 12 | /** 13 | * 14 | * @author songwenjun 15 | * 16 | */ 17 | public class ConsistentHashRouter{ 18 | private SortedMap ring = new TreeMap(); 19 | private MD5Hash hashfunction = new MD5Hash(); 20 | 21 | public ConsistentHashRouter(Collection pNodes, int vnodeCount) { 22 | for (PhysicalNode pNode : pNodes) { 23 | addNode(pNode,vnodeCount); 24 | } 25 | } 26 | 27 | public void addNode(PhysicalNode pNode, int vnodeCount){ 28 | int existingReplicas = getReplicas(pNode.toString()); 29 | for(int i=0;i it = ring.keySet().iterator(); 37 | while (it.hasNext()) { 38 | Long key = it.next(); 39 | VirtualNode virtualNode = ring.get(key); 40 | if (virtualNode.matches(pNode.toString())) { 41 | it.remove(); 42 | } 43 | } 44 | } 45 | 46 | public PhysicalNode getNode(String key){ 47 | if (ring.isEmpty()) { 48 | return null; 49 | } 50 | Long hashKey=hashfunction.hash(key); 51 | SortedMap tailMap= ring.tailMap(hashKey); 52 | hashKey = tailMap!=null&&!tailMap.isEmpty() ?tailMap.firstKey():ring.firstKey(); 53 | return ring.get(hashKey).getParent(); 54 | } 55 | 56 | public int getReplicas(String nodeName) { 57 | int replicas = 0; 58 | for (VirtualNode node : ring.values()) { 59 | if (node.matches(nodeName)) { 60 | replicas++; 61 | } 62 | } 63 | return replicas; 64 | } 65 | 66 | private static class MD5Hash { 67 | MessageDigest instance; 68 | 69 | public MD5Hash() { 70 | try { 71 | instance = MessageDigest.getInstance("MD5"); 72 | } catch (NoSuchAlgorithmException e) { 73 | } 74 | } 75 | 76 | long hash(String key) { 77 | instance.reset(); 78 | instance.update(key.getBytes()); 79 | byte[] digest = instance.digest(); 80 | 81 | long h = 0; 82 | for (int i = 0; i < 4; i++) { 83 | h <<= 8; 84 | h |= ((int) digest[i]) & 0xFF; 85 | } 86 | return h; 87 | } 88 | }; 89 | } 90 | -------------------------------------------------------------------------------- /PhysicalNode.java: -------------------------------------------------------------------------------- 1 | package com.moe.oa.model; 2 | /** 3 | * 4 | * @author songwenjun 5 | * 6 | */ 7 | public class PhysicalNode { 8 | private String domain; 9 | private String ip; 10 | private int port; 11 | 12 | public PhysicalNode(String domain,String ip,int port){ 13 | this.domain=domain; 14 | this.ip=ip; 15 | this.port=port; 16 | } 17 | 18 | @Override 19 | public String toString() { 20 | return domain+":"+ ip+":"+ port ; 21 | } 22 | 23 | public String getDomain() { 24 | return domain; 25 | } 26 | public void setDomain(String domain) { 27 | this.domain = domain; 28 | } 29 | public String getIp() { 30 | return ip; 31 | } 32 | public void setIp(String ip) { 33 | this.ip = ip; 34 | } 35 | public int getPort() { 36 | return port; 37 | } 38 | public void setPort(int port) { 39 | this.port = port; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ConsistentHash 2 | ============== 3 | 4 | Implementing Consistent Hashing in Java 5 | 6 | Reference 7 | http://www.codeproject.com/Articles/56138/Consistent-hashing 8 | -------------------------------------------------------------------------------- /VirtualNode.java: -------------------------------------------------------------------------------- 1 | package com.moe.oa.model; 2 | /** 3 | * 4 | * @author songwenjun 5 | * 6 | */ 7 | public class VirtualNode { 8 | private int replicaNumber; 9 | private PhysicalNode parent; 10 | 11 | public VirtualNode(PhysicalNode parent, int replicaNumber) { 12 | this.replicaNumber = replicaNumber; 13 | this.parent = parent; 14 | } 15 | 16 | public boolean matches(String host) { 17 | return parent.toString().equalsIgnoreCase(host); 18 | } 19 | 20 | @Override 21 | public String toString() { 22 | return parent.toString().toLowerCase() + ":" + replicaNumber; 23 | } 24 | 25 | public int getReplicaNumber() { 26 | return replicaNumber; 27 | } 28 | 29 | public void setReplicaNumber(int replicaNumber) { 30 | this.replicaNumber = replicaNumber; 31 | } 32 | 33 | public PhysicalNode getParent() { 34 | return parent; 35 | } 36 | } 37 | --------------------------------------------------------------------------------