【代码片】一致性哈希算法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
posted @ 2024-04-08 20:39  niaonao  阅读(0)  评论(0)    收藏  举报  来源