【代码片】一致性哈希算法Java实现
在实现负载均衡策略时,一致性哈希算法相对比较稳定。Java实现代码如下所示。
对于一组服务节点List nodes,通过初始化哈希环,引入虚拟节点。
在响应大量请求时,服务基本能均匀打到各个服务节点。
避免单个服务接收请求占比过大。
import java.util.*;
import java.util.concurrent.*;
import java.util.zip.*;
/**
* 一致性哈希策略
*/
public class ConsistentHashingUtil {
// 虚拟节点数量,增加虚拟节点可以使分布更加均匀
private static final int VIRTUAL_NODES = 10;
// 一致性哈希环
private TreeMap<Long, String> hashCircle = new TreeMap<>();
// 原始节点列表
private List<String> nodes = new ArrayList<>();
public ConsistentHashingUtil(List<String> nodeList) {
this.nodes.addAll(nodeList);
init();
}
// 初始化一致性哈希环
private void init() {
for (String node : nodes) {
addNode(node);
}
}
// 添加节点
public void addNode(String node) {
for (int i = 0; i < VIRTUAL_NODES; i++) {
String virtualNodeName = node + "&&VN" + i;
long hash = hash(virtualNodeName);
hashCircle.put(hash, node);
}
}
// 删除节点
public void removeNode(String node) {
for (int i = 0; i < VIRTUAL_NODES; i++) {
String virtualNodeName = node + "&&VN" + i;
long hash = hash(virtualNodeName);
hashCircle.remove(hash);
}
}
// 获取指定token对应的节点
public String getNode(String task) {
long hash = hash(task);
SortedMap<Long, String> tailMap = hashCircle.tailMap(hash);
Long nodeHash = !tailMap.isEmpty() ? tailMap.firstKey() : hashCircle.firstKey();
return hashCircle.get(nodeHash);
}
// 使用CRC32哈希算法
private long hash(String key) {
CRC32 crc32 = new CRC32();
crc32.update(key.getBytes());
return crc32.getValue();
}
public static void main(String[] args) {
// 测试节点:假设有四个服务节点可供使用
List<String> nodes = Arrays.asList("sk-token-192.168.1.101", "sk-token-192.168.1.102", "sk-token-192.168.2.211" , "sk-token-192.168.2.212");
ConcurrentHashMap<String, Integer> countMap = new ConcurrentHashMap(){{
put("sk-token-192.168.1.101", 0);
put("sk-token-192.168.1.102", 0);
put("sk-token-192.168.2.211", 0);
put("sk-token-192.168.2.212", 0);
}};
ConsistentHashingUtil consistentHashing = new ConsistentHashingUtil(nodes);
// 测试任务:假设同一时刻有100个请求
List<String> requestList = new ArrayList<>();
for (int i = 0; i < 100; i++) {
requestList.add(String.valueOf(i));
}
ExecutorService executorService = Executors.newFixedThreadPool(10);
for (String request : requestList) {
executorService.submit(() -> {
String node = consistentHashing.getNode(request);
if (null != countMap.get(node)) {
countMap.put(node, countMap.get(node) + 1);
}
System.out.println("Request: " + request + ", Node: " + node);
});
}
executorService.shutdown();
//等待直到所有任务完成
try {
executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.MINUTES);
countMap.forEach((k, v) -> {
System.out.println("Node: " + k + ", Count: " + v);
});
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
执行结果
Request: 3, Node: sk-token-192.168.1.102
Request: 7, Node: sk-token-192.168.1.102
Request: 0, Node: sk-token-192.168.1.101
Request: 1, Node: sk-token-192.168.1.101
Request: 6, Node: sk-token-192.168.2.212
Request: 8, Node: sk-token-192.168.1.101
Request: 2, Node: sk-token-192.168.2.212
Request: 4, Node: sk-token-192.168.1.101
Request: 9, Node: sk-token-192.168.1.102
Request: 5, Node: sk-token-192.168.1.101
Request: 18, Node: sk-token-192.168.2.212
Request: 17, Node: sk-token-192.168.2.211
Request: 20, Node: sk-token-192.168.1.101
Request: 16, Node: sk-token-192.168.2.211
Request: 15, Node: sk-token-192.168.2.211
Request: 14, Node: sk-token-192.168.2.211
Request: 13, Node: sk-token-192.168.2.211
Request: 12, Node: sk-token-192.168.2.212
Request: 10, Node: sk-token-192.168.2.211
Request: 11, Node: sk-token-192.168.1.102
Request: 28, Node: sk-token-192.168.1.101
Request: 27, Node: sk-token-192.168.1.101
Request: 30, Node: sk-token-192.168.1.102
Request: 31, Node: sk-token-192.168.1.101
Request: 26, Node: sk-token-192.168.1.101
Request: 25, Node: sk-token-192.168.1.101
Request: 24, Node: sk-token-192.168.1.102
Request: 23, Node: sk-token-192.168.1.101
Request: 22, Node: sk-token-192.168.1.101
Request: 21, Node: sk-token-192.168.1.102
Request: 19, Node: sk-token-192.168.1.102
Request: 39, Node: sk-token-192.168.1.101
Request: 38, Node: sk-token-192.168.2.211
Request: 37, Node: sk-token-192.168.1.102
Request: 36, Node: sk-token-192.168.1.102
Request: 35, Node: sk-token-192.168.1.102
Request: 34, Node: sk-token-192.168.1.102
Request: 45, Node: sk-token-192.168.2.212
Request: 33, Node: sk-token-192.168.1.102
Request: 32, Node: sk-token-192.168.1.101
Request: 29, Node: sk-token-192.168.1.101
Request: 49, Node: sk-token-192.168.2.211
Request: 48, Node: sk-token-192.168.2.211
Request: 47, Node: sk-token-192.168.2.211
Request: 46, Node: sk-token-192.168.2.211
Request: 44, Node: sk-token-192.168.1.102
Request: 43, Node: sk-token-192.168.2.211
Request: 42, Node: sk-token-192.168.2.211
Request: 41, Node: sk-token-192.168.2.212
Request: 40, Node: sk-token-192.168.1.102
Request: 58, Node: sk-token-192.168.2.212
Request: 57, Node: sk-token-192.168.1.101
Request: 56, Node: sk-token-192.168.2.211
Request: 55, Node: sk-token-192.168.2.212
Request: 54, Node: sk-token-192.168.2.212
Request: 53, Node: sk-token-192.168.1.101
Request: 52, Node: sk-token-192.168.2.211
Request: 51, Node: sk-token-192.168.2.212
Request: 50, Node: sk-token-192.168.2.212
Request: 67, Node: sk-token-192.168.1.102
Request: 68, Node: sk-token-192.168.1.102
Request: 66, Node: sk-token-192.168.1.102
Request: 65, Node: sk-token-192.168.2.211
Request: 64, Node: sk-token-192.168.1.101
Request: 63, Node: sk-token-192.168.1.102
Request: 62, Node: sk-token-192.168.1.102
Request: 61, Node: sk-token-192.168.2.211
Request: 60, Node: sk-token-192.168.1.101
Request: 59, Node: sk-token-192.168.2.212
Request: 77, Node: sk-token-192.168.1.102
Request: 76, Node: sk-token-192.168.2.212
Request: 75, Node: sk-token-192.168.1.101
Request: 74, Node: sk-token-192.168.1.101
Request: 73, Node: sk-token-192.168.1.102
Request: 72, Node: sk-token-192.168.2.212
Request: 71, Node: sk-token-192.168.1.101
Request: 70, Node: sk-token-192.168.1.101
Request: 69, Node: sk-token-192.168.2.211
Request: 86, Node: sk-token-192.168.2.211
Request: 85, Node: sk-token-192.168.1.102
Request: 84, Node: sk-token-192.168.1.102
Request: 83, Node: sk-token-192.168.1.101
Request: 82, Node: sk-token-192.168.2.211
Request: 81, Node: sk-token-192.168.1.102
Request: 80, Node: sk-token-192.168.1.102
Request: 79, Node: sk-token-192.168.1.102
Request: 78, Node: sk-token-192.168.1.101
Request: 95, Node: sk-token-192.168.2.212
Request: 94, Node: sk-token-192.168.1.102
Request: 93, Node: sk-token-192.168.1.101
Request: 92, Node: sk-token-192.168.1.101
Request: 91, Node: sk-token-192.168.2.212
Request: 90, Node: sk-token-192.168.1.102
Request: 89, Node: sk-token-192.168.1.102
Request: 88, Node: sk-token-192.168.1.101
Request: 87, Node: sk-token-192.168.1.101
Request: 99, Node: sk-token-192.168.1.101
Request: 98, Node: sk-token-192.168.1.102
Request: 97, Node: sk-token-192.168.1.101
Request: 96, Node: sk-token-192.168.1.101
Node: sk-token-192.168.1.101, Count: 33
Node: sk-token-192.168.2.211, Count: 20
Node: sk-token-192.168.1.102, Count: 29
Node: sk-token-192.168.2.212, Count: 15

浙公网安备 33010602011771号