Zookeeper的一致性哈希的负载均衡算法

1.什么是一致性哈希

一致性哈希(Consistent Hashing)是一种分布式系统中的哈希算法,主要用于将请求或数据均匀分配到多个服务器或节点上。它解决了当服务器节点发生变化(如增加或减少服务器)时,如何最小化数据迁移的问题。一致性哈希的主要优点是高效和负载均衡,尤其在动态变化的分布式系统中表现出色。

  1. 哈希环: 一致性哈希将所有可能的哈希值组织成一个逻辑上的环(Hash Ring)。环的大小通常为哈希函数的输出范围。例如,使用SHA-1哈希函数,环的大小就是2^160。

  2. 节点位置: 每个节点(服务器)通过某种哈希函数(如SHA-1)计算出其位置(哈希值),并映射到环上。这些节点按哈希值的大小排列。

  3. 数据存储位置: 数据项通过同样的哈希函数计算哈希值,并映射到环上。数据项将被存储在顺时针方向最近的节点上。例如,如果数据项D的哈希值为H(D),它将存储在第一个哈希值大于或等于H(D)的节点上。

  4. 节点变动的影响

    • 增加节点:只会影响新增节点顺时针方向最近的一部分数据,因为这些数据项会被重新分配给新增的节点。
    • 减少节点:只会影响被移除节点上的数据,这些数据会重新分配给其他节点。
  5. 虚拟节点: 为了提高负载均衡性,一致性哈希引入了虚拟节点的概念。每个物理节点会映射到环上的多个位置(即多个虚拟节点)。这样可以更均匀地分布数据,从而避免数据倾斜的问题。

2.一致性哈希的好处

  • 扩展性好:节点数量变化时,只需重新分配极少部分的数据。
  • 负载均衡:通过虚拟节点的引入,能够更好地均衡负载。
  • 高可用性:节点故障时,只会影响该节点上的数据,其他节点不受影响。

3.负载均衡

ZooKeeper内部没有一个负载均衡器,但是我们在生产中实际要避免某个几点过多的使用,所以这个时候就需要进行负载均衡。

我们常见的负载均衡策略有几种:

  • 随机
  • 轮询
  • 一致性哈希

这一次我们就来说说一致性哈希的负载均衡。

4.代码

我们使用抽象类来使用完成这个算法

public abstract class AbstractConsistentHash {

    // 添加节点到哈希环
    protected abstract void add(long key, String value);

    // 排序哈希环节点
    protected void sort() {}

    // 根据对象哈希值获取第一个匹配的节点
    protected abstract String getFirstNodeValue(String key);

    // 处理之前的清理操作
    protected abstract void processBefore();

    // 添加节点列表到哈希环
    public synchronized void addNodes(List<String> values) {
        processBefore();
        for (String value : values) {
            add(hash(value), value);
        }
        sort();
    }

    // 计算哈希值
    public long hash(String value) {
        MessageDigest md5;
        try {
            md5 = MessageDigest.getInstance("MD5");
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(" ", e);
        }
        md5.reset();
        byte[] keyBytes;
        try {
            keyBytes = value.getBytes("UTF-8");
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(" " + value, e);
        }
        md5.update(keyBytes);
        byte[] digest = md5.digest();
        long hashCode = ((long) (digest[3] & 0xFF) << 24) | ((long) (digest[2] & 0xFF) << 16) | ((long) (digest[1] & 0xFF) << 8) | (digest[0] & 0xFF);
        return hashCode & 0xffffffffL;
    }
}

使用TreeMap 的数据结构

public class TreeMapConsistentHash extends AbstractConsistentHash {

    private TreeMap<Long, String> treeMap = new TreeMap<>();
    private static final int NODE_SIZE = 2;

    @Override
    protected void add(long key, String value) {
        for (int i = 0; i < NODE_SIZE; i++) {
            treeMap.put(super.hash("node" + key + i), value);
        }
        treeMap.put(key, value);
    }

    @Override
    protected String getFirstNodeValue(String key) {
        Long hash = super.hash(key);
        SortedMap<Long, String> tailMap = treeMap.tailMap(hash);
        if (!tailMap.isEmpty()) {
            return tailMap.get(tailMap.firstKey());
        }
        if (treeMap.size() == 0) {
            System.out.println("有问题");
        }
        return treeMap.firstEntry().getValue();
    }

    @Override
    protected void processBefore() {
        treeMap.clear();
    }
}

测试类

public class OptimizedConsistentHashExample {
    public static void main(String[] args) {
        // 定义节点列表
        List<String> nodes = Arrays.asList("Node1", "Node2", "Node3");

        // 创建一致性哈希对象
        TreeMapConsistentHash consistentHash = new TreeMapConsistentHash();

        // 将节点添加到哈希环中(只做一次)
        consistentHash.addNodes(nodes);

        // 现在哈希环已经包含了所有节点,可以对键进行路由
        String nodeForKey1 = consistentHash.getFirstNodeValue("key1");
        String nodeForKey2 = consistentHash.getFirstNodeValue("Key2");

        System.out.println("Key1 is routed to: " + nodeForKey1);
        System.out.println("Key2 is routed to: " + nodeForKey2);
    }
}

5.结论

大白话简单的说一下就是:

就是通过md5求出node节点的哈希值,将哈希值添加到Treemap中 key为哈希值,value 为节点名字;通过传进来的id生成的哈希 ,把Treemap.tail()方法将其转化了sortset ,然后获得sortset中的第一个String值,这个值就是我们要选中的节点的名字;

  • 19
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值