源地址hash算法
应用场景:网关负载均衡
源地址 Hash 是根据客户端的 IP 地址(或请求路径),通过 Hash 函数的运算把IP 地址(或请求路径)转换为一个固定的数字。根据“Hash”后的数字,对服务器列表进行取模运算,得到服务器的序号。
这种算法的好处是,同一个 IP 地址所选择的服务器总是相同的。相同服务器本地缓存数据,对于有状态的服务来说,每次访问都会命中缓存。
计算公式:ip.hashCode() % serverCount = serverNum

public static void main(String[] args) {
    // 服务端机器IP地址、服务序号
    Map<Integer, String> serverList = new HashMap<>();
    serverList.put(0, "1.1.1.2");
    serverList.put(1, "1.1.1.3");
    serverList.put(2, "1.1.1.4");
    serverList.put(3, "1.1.1.5");

    // 客户端机器IP地址
    String clientList = "0.1.1.2";

    // 源地址hash计算
    Integer index = clientList.hashCode() % serverList.size();
    System.out.println("访问的是服务端的机器序号为:" + serverList.get(index));
}

优点:实现简单
缺点:当服务器数量发生变化,容易造成缓存雪崩
一致性hash算法
一致性哈希算法也是使用取模的方法,但是取模算法是对服务器的数量进行取模,而一致性哈希算法是对 2^32 取模,具体步骤如下:
步骤一:一致性哈希算法将整个哈希值空间按照顺时针方向组织成一个虚拟的圆环,称为 Hash 环(232个点组成,范围是0-232-1);

步骤二:接着将各个服务器使用 Hash 函数进行哈希,具体可以选择服务器的IP或主机名作为关键字进行哈希,从而确定每台机器在哈希环上的位置
计算公式:哈希算法:ip.hashCode() % 2^32

步骤三:最后使用算法定位数据访问到相应服务器:将数据key(可以是客户端的IP,服务名等)使用相同的函数Hash计算出哈希值,并确定此数据在环上的位置,从此位置沿环顺时针寻找,第一台遇到的服务器就是其应该定位到的服务器

优点:
前面提到,如果简单对服务器数量进行取模,那么当服务器数量发生变化时,会产生缓存的雪崩,从而很有可能导致系统崩溃,而使用一致性哈希算法就可以很好的解决这个问题,因为一致性Hash算法对于节点的增减都只需重定位环空间中的一小部分数据,只有部分缓存会失效,不至于将所有压力都在同一时间集中到后端服务器上,具有较好的容错性和可扩展性。
hash 环的倾斜与虚拟节点
一致性哈希算法在服务节点太少的情况下,容易因为节点分部不均匀而造成数据倾斜问题,也就是被缓存的对象大部分集中缓存在某一台服务器上,从而出现数据分布不均匀的情况,这种情况就称为 hash 环的倾斜。如下图所示:

hash 环的倾斜在极端情况下,仍然有可能引起系统的崩溃,为了解决这种数据倾斜问题,一致性哈希算法引入了虚拟节点机制,即对每一个服务节点计算多个哈希,每个计算结果位置都放置一个此服务节点,称为虚拟节点,一个实际物理节点可以对应多个虚拟节点,虚拟节点越多,hash环上的节点就越多,缓存被均匀分布的概率就越大,hash环倾斜所带来的影响就越小,同时数据定位算法不变,只是多了一步虚拟节点到实际节点的映射。具体做法可以在服务器ip或主机名的后面增加编号来实现,加入虚拟节点以后的hash环如下: