四层负载均衡原理

2020-05-25 00:00:00 算法 节点 就会 负载 负载均衡

四层负载均衡是指面向五元组的负载均衡,四层指的是TCP和UDP所代表的四层。也就是连接层面的负载均衡。

在四层的负载均衡的领域(典型的是LVS),一个外部的IP和端口对应一个服务,但是实际的后端会有不止一个服务器在响应这个服务。这个外部的IP和端口组成的服务就叫做一个服务,一个服务用vip:vport来表示。Vip就代表外部的IP,也叫做EIP(external IP),之所以可以叫做VIP是因为外部的IP并不是真正响应这个服务的IP,只是对这个服务入口的一个统一的代表,所以叫做virtual IP。

一个VIP:VPORT会对应多个RIP:RPORT,RIP就是真实的服务这个请求的后端服务器的IP,RIP的意思是Real IP,这个真实的后端服务器就叫做RS(Real Server)。

一个典型的负载均衡的场景,一个VIP:VPORT,对应多个RIP:RPORT。每个请求来的时候,随机的被分配到一个RIP:RPORT。完成这个映射关系的节点就叫做LB(负载均衡)节点。

看起来是很完美的一个架构,但是稍微一想就会发现问题。因为网络中的流量是以数据包的形式到达LB的,LB如果完全的随机分配每一个数据包,就会导致同一个TCP流的信息被分配到不同的RS,这样肯定会导致流的混乱,RS不能处理不是自己建立的TCP流的情况的(UDP类似),所以整个系统就不能运行。

这就要求同一个流(五元组)要被分配到同一个RS,而不是按照每一个数据包的维度进行调度。为了达到这个目的,有两个思路,一个是使用流表,当一个流的五元组的个包到达的时候,使用任何的负载均衡算法选择一个RS(比如Round Robin或者Least Connection),然后将这个选择了哪个RS的信息存储在流表,之后这个流的数据包再到达的时候,先查流表,查到了就直接拿到了后端的RS,直接转发给记录的RS,这样同一个五元组就可以保证落到同一个RS上了。另外一个思路是使用哈希来作为负载均衡的算法。因为我们的目标是同一个五元组的数据包都转发到同一个后端RS,如果每个数据包都使用同样的哈希算法同样的输入进行计算,得到的RS的选择就是一样的。这个哈希算法的输入就是五元组。使用五元组作为哈希算法的输入,对每一个数据包都提取五元组进行同样的哈希计算,就可以同样的满足每一个流都落到同一个RS,并且还不需要维护一个流表项。所以哈希的算法得到了广泛的应用,交换机普遍支持的ECMP的负载均衡的方式都是采用的哈希算法。

哈希算法看起来是完美的,但是再深入的思考就会发现哈希算法也要面临一个严重的问题。就是当一个RS掉线的时候,哈希重算的问题。典型的比如LVS,当一个RS掉线,LVS会重算所有的五元组,就是原来五元组落到一个RS上,重算之后就落到了另外一个RS上,这就导致了当增加或者去除一个RS的时候,所有的流都会断开连接。

这显然是不能接受的。解决这个问题,有一个简单的办法就是仍然使用流表,因为流表已经记录了到达每一个五元组到达哪一个RS的信息,每次都是查询,只要LB节点不挂掉,已有的流都会被正确的分配,只是原来目标是掉线的RS的那个流的所有的流都会断开,因为已经找不到那个RS了。RS不存在了,对应的五元组显然也没有存在的意义了。

流表在大部分情况下是一个终极的解决方案。但是流表也有自己的问题,流表和哈希是解决五元组问题的两种思路,一个是数学的方式解决,一个是资源的方式解决。数学的方式有数学的问题,就是重算。资源的方式有资源的问题,就是重度消耗资源。不但是消耗本地的资源,还有一个极其严重的问题是消耗网络资源。因为依赖流表,当一个LB节点挂掉的时候,LB上的流信息没有办法在其他节点复原,所以就需要一个流同步的操作。这个流同步尤其是全量的流同步,在很多时候是一个根本无法解决的问题。实时性和重度的网络资源消耗都会成为制约这个方案的瓶颈因素。但是两种方案并不是都没有解决的方法。

负载均衡常用的方法有三种,一种是RR,一种是LC,一种是哈希。

RR是round robin,每次轮询的选择一个后端。由于是请求层面的轮询,所以并不区分是长连接还是短链接。RR模式的轮询在负载比较高的情况下会有两个问题。一个是轮询RS 并不区分连接,而实际上有的连接是长连接,有的是短链接,这样就会造成不同RS上的连接数不一样的情况,但是这种情况在大并发的情况下只会导致某一个节点比其他节点更快的到达满负载的情况。由于到了满负载,持续的丢失响应一定的次数,LB就会从upstream中摘掉这个RS,这个取决于LB配置的健康检测的策略。就是LB什么时候认为RS是不健康的。另外一个更加严重的问题是雪崩效应,当一个节点负载比较严重的时候,RR并不会放过这个节点,而是一视同仁的对待它,这个节点就会更加超负荷运行,这个节点由于负载太高,挂掉之后,RR的机制又会让剩下的RS的某一个挂掉。如此,所有的RS会被交替的挂掉。导致整个RS后端的大负载永远无法达到。RR算法在RS的负载能力不一致的时候尤其严重。

哈希算法的好处是完全的面向五元组。但是缺点也跟RR是一样的,哈希算法也完全不会考虑RS的不同的负载能力,由于也不区分长连接还是短连接,所以哈希算法也会导致RS的负载不均衡加剧的情况。哈希算法的RS总吞吐很高概率也是跑不满的。

LC算法部分的考虑了后端的负载情况,重要的是考虑长短连接的情况。每次只会选择连接数少的RS进行连接,这样有效的通过选择比较空闲的RS进行负载分配来完成请求。所以当RS的服务能力大体相当的情况下,LC算法是高概率达到大吞吐的算法。但是LC算法也并没有考虑不同的RS负载能力的情况。

一个优化版本的负载均衡算法是要考虑RS的负载能力,在失败发生的时候动态的调整这个节点被分配的连接的数量。大部分的开源的实现中并不提供这样的优化实现,而通常在企业的内部,会做此种类型的优化。由于真实的应用场景下,大部分的情况RS的负载是不会满的,所以在实际的使用上,这三种负载均衡的算法并没有太大的区别。

相关文章