一致性hash(整理版)
简单解释:
简单解释一致性hash的原理:网上通篇都是用服务器做的举例,我这里也如此,主要是便于理解。
通常:有N个客户端请求服务器,假设有M台web服务器,通常为了均衡访问会进行N%M的取模,然后分配到不同的缓存服务器访问。问题是一旦缓存服务器增加或减少时缓存的命中率被打乱,因为取模发生了变化。例A的访问分配到了M1上,假设服务器增减则有可能会被分配到M3上(M3为举例),那么M1上的原有缓存失效,M3需要重新建立A访问的缓存。
一致性Hash:有N个客户端请求服务器,假设有M台web服务器,把其中一个请求做HASH,M台服务器也做HASH,采用Sortedmap的tailMap特性,检索顺时针最近的一台服务器作为缓存服务器;
例:其中一个请求HASHCODE=254,M台服务器HASH后分别为1、58、648...(数字为举例),那么通过Sortedmap的tailMap特性可以找到648以及后面的服务器,则把最近的648作为缓存服务器。当然 如果tailMap找到的为NULL,则从Sortedmap的第一条key作为缓存服务器(保证map是一个环装)
简单代码:
完整代码示意-摘自互联网的代码(整理后),可以参考。
package com.yiibai; import java.util.*; public class TreeMapDemo { public static void main(String[] args) { // creating maps TreeMap<Integer, String> treemap = new TreeMap<Integer, String>(); SortedMap<Integer, String> treemapincl = new TreeMap<Integer, String>(); // populating tree map treemap.put(2, "two"); treemap.put(1, "one"); treemap.put(3, "three"); treemap.put(6, "six"); treemap.put(5, "five"); System.out.println("Getting tail map"); treemapincl=treemap.tailMap(3); System.out.println("Tail map values: "+treemapincl); } }
完整代码示意-摘自互联网的代码(整理后),可以参考。
import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.SortedMap; import java.util.TreeMap; import org.apache.commons.codec.digest.DigestUtils; public class VNode { private SortedMap<Integer, String> serverNodeMap = null; private final static int VIRTUAL_NODE_NUMBER = 5; public void getServerNodeWithoutVirtualNode(List<String> servers) { serverNodeMap = new TreeMap<Integer, String>(); for (String string : servers) { serverNodeMap.put(hash(string), string); } } public void getServerNodeWithVirtualNode(List<String> servers) { serverNodeMap = new TreeMap<Integer, String>(); for (String string : servers) { for (int i = 0; i < VIRTUAL_NODE_NUMBER; i++) { String virtualNodeName = string + ":" + i; serverNodeMap.put(hash(virtualNodeName), string); } } } public String getServerName(String data) { int dataHash = hash(data); SortedMap<Integer, String> subMap = serverNodeMap.tailMap(dataHash); int serverHash = 0; if (subMap == null || subMap.size() == 0) { serverHash = serverNodeMap.firstKey(); } else { serverHash = subMap.firstKey(); } String serverName = serverNodeMap.get(serverHash); return serverName; } /** * hash计算,这里使用md5后取hashcode,这个md5需要依赖apache的codec包 * @param str * @return */ public int hash(String str) { //System.out.println(str+"的hashcode="+DigestUtils.md5Hex(str).hashCode()); return DigestUtils.md5Hex(str).hashCode(); } public static void main(String[] args) { List<String> servers = new ArrayList<String>(); servers.add("192.168.1.1"); servers.add("192.168.1.2"); servers.add("192.168.1.3"); servers.add("192.168.1.4"); servers.add("192.168.1.5"); servers.add("192.168.1.6"); List<String> datas = new ArrayList<String>(); datas.add("A"); datas.add("B"); datas.add("C"); datas.add("D"); datas.add("E"); datas.add("F"); datas.add("G"); datas.add("H"); datas.add("I"); VNode consistentHash = new VNode(); System.out.println("没有虚拟节点的情况:"); consistentHash.getServerNodeWithoutVirtualNode(servers); consistentHash.printDataAndServerNode(servers, datas, consistentHash); System.out.println("有虚拟节点的情况:"); consistentHash.getServerNodeWithVirtualNode(servers); consistentHash.printDataAndServerNode(servers, datas, consistentHash); servers.add("192.168.1.7"); System.out.println("增加第一个一个节点后:"); System.out.println("没有虚拟节点的情况:"); consistentHash.getServerNodeWithoutVirtualNode(servers); consistentHash.printDataAndServerNode(servers, datas, consistentHash); System.out.println("有虚拟节点的情况:"); consistentHash.getServerNodeWithVirtualNode(servers); consistentHash.printDataAndServerNode(servers, datas, consistentHash); servers.remove(0); System.out.println("移除第一个一个节点后:"); System.out.println("没有虚拟节点的情况:"); consistentHash.getServerNodeWithoutVirtualNode(servers); consistentHash.printDataAndServerNode(servers, datas, consistentHash); System.out.println("有虚拟节点的情况:"); consistentHash.getServerNodeWithVirtualNode(servers); consistentHash.printDataAndServerNode(servers, datas, consistentHash); } public void printDataAndServerNode(List<String> servers, List<String> datas,VNode consistentHash) { Map<String, String> result = new HashMap<String, String>(); for (String data : datas) { String serverName = consistentHash.getServerName(data); if (!result.containsKey(serverName)) { result.put(serverName, data); } else { result.put(serverName, result.get(serverName) + "," + data); } } for (Entry<String, String> entry : result.entrySet()) { System.out.println(entry.getKey()+":"+entry.getValue()); } } }