分布式系统关注点——仅需这一篇,吃透「负载均衡」妥妥的

本文长度为3426字,预计读完需1.2MB流量,建议阅读9分钟。


  上一篇《分布式系统关注点——初识「高可用」》咱们对「高可用」有了一个初步认识,其中认为「负载均衡」是「高可用」的核心工做。那么,本篇将经过图文并茂的方式,来描述出每一种负载均衡策略的完整样貌。算法

1、「负载均衡」是什么

正如题图所示的这样,由一个独立的统一入口来收敛流量,再作二次分发的过程就是「负载均衡」,它的本质和「分布式系统」同样,是「分治」。segmentfault

若是你们习惯了开车的时候用一些导航软件,咱们会发现,导航软件的推荐路线方案会有一个数量的上限,好比3条、5条。所以,其实本质上它也起到了一个相似「负载均衡」的做用,由于若是只能取Top3的通畅路线,天然拥堵严重的路线就没法推荐给你了,使得车流的压力被分摊到了相对空闲的路线上。服务器

在软件系统中也是同样的道理,为了不流量分摊不均,形成局部节点负载过大(如CPU吃紧等),因此引入一个独立的统一入口来作相似上面的“导航”的工做。可是,软件系统中的「负载均衡」与导航的不一样在于,导航是一个柔性策略,最终仍是须要使用者作选择,而前者则不一样。微信

怎么均衡的背后是策略在起做用,而策略的背后是由某些算法或者说逻辑来组成的。好比,导航中的算法属于「路径规划」范畴,在这个范畴内又细分为「静态路径规划」和「动态路径规划」,而且,在不一样的分支下还有各类具体计算的算法实现,如Dijikstra、A*等。一样的,在软件系统中的负载均衡,也有不少算法或者说逻辑在支撑着这些策略,巧的是也有静态和动态之分。 架构


2、经常使用「负载均衡」策略图解

下面来罗列一下平常工做中最多见的5种策略。并发

01 轮询

图片描述

这是最经常使用也最简单策略,平均分配,人人都有、一人一次。大体的代码以下。负载均衡

int  globalIndex = 0;   //注意是全局变量,不是局部变量。

try
{

    return servers[globalIndex];
}
finally
{
    globalIndex++;
    if (globalIndex == 3)
        globalIndex = 0;
}

02 加权轮询

图片描述

在轮询的基础上,增长了一个权重的概念。权重是一个泛化后的概念,能够用任意方式来体现,本质上是一个能者多劳思想。好比,能够根据宿主的性能差别配置不一样的权重。大体的代码以下。分布式

int matchedIndex = -1;
int total = 0;

for (int i = 0; i < servers.Length; i++)
{
      servers[i].cur_weight += servers[i].weight;//①每次循环的时候作自增(步长=权重值)
      total += servers[i].weight;//②将每一个节点的权重值累加到汇总值中
      if (matchedIndex == -1 || servers[matchedIndex].cur_weight < servers[i].cur_weight)
       //③若是 当前节点的自增数 > 当前待返回节点的自增数,则覆盖。
      {
            matchedIndex = i;
      }
}

servers[matchedIndex].cur_weight -= total;
//④被选取的节点减去②的汇总值,以下降下一次被选举时的初始权重值。
return servers[matchedIndex];

这段代码的过程以下图的表格。"()"中的数字就是自增数,代码中的cur_weight。函数

图片描述

值得注意的是,加权轮询自己还有不一样的实现方式,虽然说最终的比例都是2:1:2。可是在请求送达的前后顺序上能够全部不一样。好比「5-4,3,2-1」和上面的案例相比,最终比例是同样的,可是效果不一样。「5-4,3,2-1」更容易产生并发问题,致使服务端拥塞,且这个问题随着权重数字越大越严重。例子:10:5:3的结果是「18-17-16-15-14-13-12-11-10-9,8-7-6-5-4,3-2-1」性能

03 最少链接数

图片描述

这是一种根据实时的负载状况,进行动态负载均衡的方式。维护好活动中的链接数量,而后取最小的返回便可。大体的代码以下。

var matchedServer = servers.orderBy(e => e.active_conns).first();

matchedServer.active_conns += 1;

return matchedServer;

//在链接关闭时还需对active_conns作减1的动做。

04 最快响应

图片描述

这也是一种动态负载均衡策略,它的本质是根据每一个节点对过去一段时间内的响应状况来分配,响应越快分配的越多。具体的运做方式也有不少,上图的这种能够理解为,将最近一段时间的请求耗时的平均值记录下来,结合前面的「加权轮询」来处理,因此等价于2:1:3的加权轮询。

题外话:通常来讲,同机房下的延迟基本没什么差别,响应时间的差别主要在服务的处理能力上。若是在跨地域(例:浙江->上海,仍是浙江->北京)的一些请求处理中运用,大多数状况会使用定时「ping」的方式来获取延迟状况,由于是OSI的L3转发,数据更干净,准确性更高。

05 Hash法

图片描述

hash法的负载均衡与以前的几种不一样在于,它的结果是由客户端决定的。经过客户端带来的某个标识通过一个标准化的散列函数进行打散分摊

上图中的散列函数运用的是最简单粗暴的「取余法」。

题外话:散列函数除了取余以外,还有诸如「变基」、「折叠」、「平方取中法」等等,此处不作展开,有兴趣的小伙伴可自行查阅资料。

另外,被求余的参数其实能够是任意的,只要最终转化成一个整数参与运算便可。最经常使用的应该是用来源ip地址做为参数,这样能够确保相同的客户端请求尽量落在同一台服务器上。

3、经常使用「负载均衡」策略优缺点和适用场景

咱们知道,没有完美的事物,负载均衡策略也是同样。上面列举的这些最经常使用的策略也有各自的优缺点和适用场景,我稍做了整理,以下。

图片描述

这些负载均衡算法之因此经常使用也是由于简单,想要更优的效果,必然就须要更高的复杂度。好比,能够将简单的策略组合使用、或者经过更多维度的数据采样来综合评估、甚至是基于进行数据挖掘后的预测算法来作。

4、用「健康探测」来保障高可用

不论是什么样的策略,不免会遇到机器故障或者程序故障的状况。因此要确保负载均衡能更好的起到效果,还须要结合一些「健康探测」机制。定时的去探测服务端是否是还能连上,响应是否是超出预期的慢。若是节点属于“不可用”的状态的话,须要将这个节点临时从待选取列表中移除,以提升可用性。通常经常使用的「健康探测」方式有3种。

01 HTTP探测

使用Get/Post的方式请求服务端的某个固定的URL,判断返回的内容是否符合预期。通常使用Http状态码、response中的内容来判断。

02 TCP探测

基于Tcp的三次握手机制来探测指定的IP + 端口。最佳实践能够借鉴阿里云的SLB机制,以下图。

图片描述
▲图片来源于阿里云,版权归原做者全部

值得注意的是,为了尽早释放链接,在三次握手结束后立马跟上RST来中断TCP链接。

03 UDP探测

可能有部分应用使用的UDP协议。在此协议下能够经过报文来进行探测指定的IP + 端口。最佳实践一样能够借鉴阿里云的SLB机制,以下图。

图片描述
▲图片来源于阿里云,版权归原做者全部

结果的断定方式是:在服务端没有返回任何信息的状况下,默认正常状态。不然会返回一个ICMP的报错信息。

5、结语

用一句话来归纳负载均衡的本质是:

将请求或者说流量,以指望的规则分摊到多个操做单元上进行执行。

经过它能够实现横向扩展(scale out),将冗余的做用发挥为「高可用」。另外,还能够物尽其用,提高资源使用率。




做者:Zachary( 我的微信号:Zachary-ZF
微信公众号(首发): 跨界架构师。<-- 点击查阅近期热门文章
按期发表原创内容: 架构设计丨分布式系统丨产品丨运营丨一些深度思考
扫码加入小圈子 ↓

图片描述

相关文章
相关标签/搜索