前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >负载均衡漫谈篇

负载均衡漫谈篇

原创
作者头像
晴日飞鸢
修改2022-05-26 17:09:05
7570
修改2022-05-26 17:09:05
举报

负载均衡,相信这个名词大家都不陌生。但聊起负载均衡,可能大家相互间说的并不是同一个东西,就跟火锅似的,大家都能聊到一起,但聊了半天,发现北京人说的是铜炉火锅,云南人说的是菌菇火锅,四川人说的是红油火锅,广州人说的是清补凉。我也不知道大家说的负载均衡是不是同一个东西,若是有人要问我如何做高可用,我上来就是冗余集群+负载均衡。本文对于之前没有接触过nginx、LVS、硬件负载均衡的读者,前2节略显枯燥;遇事不决,直接第3节谢谢。

1. 客户机和服务器的架构

假定我们的服务都是C/S架构,即一侧是客户机(client),一侧是服务器(server),在本文中,所有图都默认左侧为客户机,右侧为服务器。需要注意的是,客户机和服务器的角色是相对的,一台机器既可以是服务器也可以是客户机,因为机器之间可能互相提供服务,比如A向B提供邮件服务,B向A提供文件共享服务。本文所提到的客户机和服务器,针对的是某个具体的应用,在某个具体应用的视角里,一方为客户机,一方为服务器。

1.1 单客户机单服务器(1:1)

最简单的情况,1客户机:1服务器。1台客户机仅可以访问到1台服务器,所有请求和响应都在两者之间,此处将之暂记为1:1架构。在1:1架构的基础上,引入代理服务器。根据代理服务器所在侧的不同,可分为1:1-1(反向代理)架构和1-1:1(正向代理)架构两种。

如何区分代理服务器属于正向代理还是反向代理,看代理服务器由哪一方添加。客户机方添加则为正向代理,服务器方添加则为反向代理。

添加代理服务器的目的往往有以下几项:1.保障访问安全。2.分担服务器压力。3.隐藏所在侧服务器/客户机信息。

1.2 多客户机单服务器(n:1)

客户机多过服务器的情况,n客户机:1服务器。n台客户机访问1台服务器,由1台服务器处理所有请求,此处将之暂记为n:1架构。同1.1节,引入代理,记为n:1-1架构和n-1:1架构。

1.3 单客户机多服务器(1:m)

服务器也可以多过客户机,两者数量对调,1客户机:m服务器。1台客户机可以访问到n台服务器,此处暂将之记为1:m架构。同1.1,引入代理,记为1:1-m架构和1-1:m架构。

此处需要明确,1:m代表的是1台客户机能访问到m台服务器,并非1次请求1台客户机需要同时往m台服务器都发包。通常地,1次请求客户机只发1个包给某1台服务器;如果同时给m台服务器都发,客户机会收到m个回复,这些回复可能是矛盾的或者重复的。

1.4 多客户机多服务器(n:m)

真实线上业务的情况,n客户机:m服务器。n台客户机中的每一台都可以访问到m台服务器,如下所示。同1.1节,引入代理,记为n:1-m架构和n-1:m架构。

n:m可以看作n:1架构或1:m架构的叠加 。从每一台客户机的角度来看,都是1:m的架构;从每一台服务器的角度来看,都是是n:1的架构。可以用线代知识理解:两个单位向量(1,0)(0,1),分别代表(1客户机,0服务器),(0客户机,1服务器),可以span出(n,1),(1,m)和(n,m)等向量;同时,(n,m)向量也可表达成(n,1)(1,m)向量的叠加。

n:m架构中添加代理服务器原因往往还有这几项:1.减少连接数量。可将n*m条连接减少到n+m条连接,类似于交换机在全互联拓扑起到的功能。2.集中连接方便管理。所有的连接都可以在代理上被看到和管理。3.统一进行负载均衡

n:m架构及其添加代理的架构将作为讨论负载均衡的基础。

2. 负载均衡基础

负载均衡用于分发流量。以n:m架构为例,负载均衡的作用是,在一段时间内,将n台服务器的流量尽量均匀地分摊给m台服务器。可以用积分理解负载均衡,设时间为t,任意两台服务器瞬时流量曲线为f(t) 、 g(t),负载均衡的目标并非f(t) ≈ g(t),而是在a ~ b这段时间内瞬时流量的累积值相等,即∫(a ~ b) ƒ(t) dt ≈ ∫(a ~ b) g(t) dt

如何做负载均衡,需要组合下面三个要素:1.区分流量字段。2.负载均衡算法。3.配套网络设计

2.1 区分流量字段

无论TCP/IP七层还是五层模型,前四层是一致的。一层跟数据内容无关排除在外,二三四层类似于千层饼,层层相叠,都有着固定的格式。每种格式里都包含了可标识此层数据的全部字段。

上面这些首部的添加和识别过程被称为封包和解包,封包和解包过程引用之前博客做的一张动图。

实际这些首部里的所有字段都可以用于区分流量。问题在于,区分出怎样的流量。下面是字段常用的区分用途。

在负载均衡中,主要识别的是“从哪儿来,到哪儿去” ,与之契合的是“源MAC地址”、“目MAC地址”、“源IP地址”、“目IP地址”、“源端口”、“目标端口”这些字段。

上面仅列出了二到四层的字段,除此之外,七层的字段在负载均衡也常常会被用到。七层协议HTTP、FTP、SMTP的报文格式里,都有可供负载均衡的字段,甚至协议本身支持自定义字段。比如在HTTP中,除了可以利用URL进行上下文的负载均衡,将/admin和/job这样的url分发到不同的服务器;还可以利用Request Header中的可自定义字段,进行自定义分发。这部分在nginx中将为有大量体现,此处不展开。

2.2 负载均衡算法

常见负载均衡算法如下,目前已有大量资料对每一种算法进行介绍,此处不再赘述,仅列出笔者归纳出的一些区别。

基础算法

代理必须

算法加权

目标固定

状态保存

整体开销

适用协议字段或值

轮询(Round-Robin)

×

×

×

随机(Random)

×

×

×

哈希(Hash)

×

×

源目MAC、源目IP、源目端口

最少连接数(Least-Connection)

源IP+目IP+源端口+目端口

最短响应时间(Shortest-Response Time)

×

×

ICMP协议中的时间戳

最佳处理能力(Best Processing Capacity)

×

×

SNMP协议获取到的值

2.2.1 代理必须

代理必须为算法本身是否必须依赖代理服务器实现。例如轮询,可直接在客户机实现,只需在客户机上配置服务器列表,从列表中循环取下一个即可;而最小连接数,则需要代理服务器来统计连接数量,从而得出最小连接数的服务器。

2.2.2 算法加权

算法加权为算法本身是否有加权版本。几乎所有负载均衡算法都有其加权版本,下面是轮询、随机以及它们对应的加权算法简单实现。

#encoding=utf-8

import random

import time

srvList = ["192.168.1.1:9090","192.168.1.2:9090","192.168.1.3:9090"]

srvWeight = {"192.168.1.1:9090":2}



def roundRobinGen(srvList):
    """轮询生成器"""
    index = 0
    length = len(srvList)
    while True:
        yield srvList[index]
        index=(index+1)%length



def roundRobinWithWeightGen(srvList,srvWeight):
    """加权轮询生成器"""
    indexs = []
    for i in range(len(srvList)):
        if srvList[i] in srvWeight:
            for _ in range(srvWeight[srvList[i]]):
                indexs.append(i)
        else: #如果没有在srvWeight中,则默认权重为1
            indexs.append(i)

    random.shuffle(indexs)  #这里只应用了简单随机,实际可以采取更优的方式重新均匀分布下标数组
    nowIndex = 0
    while True:
        yield srvList[indexs[nowIndex]]
        nowIndex = (nowIndex + 1) % len(indexs)

def randomGen(srvList):
    """随机生成器"""
    index = random.randint(0,len(srvList)-1)
    while True:
        yield srvList[index]
        index = random.randint(0,len(srvList)-1)

def randomWithWeightGen(srvList,srvWeight):
    """加权随机生成器"""
    borders = []
    nowSum = 0
    for i in range(len(srvList)):
        borders.append(nowSum)
        if srvList[i] in srvWeight:
            nowSum += srvWeight[srvList[i]]
        else:
            nowSum+=1

    def realIndex(index):
        """
        返回真实的Index.
        比如权重为{"192.168.1.1":3,"192.168.1.2":2},则从[0,1,2,3,4]中取随机数,
        选到0,1则为192,168.1.2,选到2,3,4则为192.168.1.1
        """
        nonlocal borders
        for i,border in enumerate(borders):
            if index>border:
                continue
            else:
                return i
                
    index = realIndex(random.randint(0,nowSum-1))
    while True:
        yield srvList[index]
        index = realIndex(random.randint(0,nowSum-1))

def test(func,*args):
    """测试函数"""
    generator = func(*args)
    for srv in generator:
        print(srv)
        time.sleep(1)

test(randomWithWeightGen,srvList,srvWeight)

2.2.3 目标固定

目标固定为算法是否能将某客户机的访问固定到某一台服务器上。如果服务器是无状态的,每次使用相同的参数返回相同的内容,则不需要目标固定。但是如果服务器会缓存用户信息,如果没有目标固定:1. 相同备份的数据同时存在于多台服务器,内存空间浪费。2. 用户的体验会变差,会遇到诸如访问网页频繁丢session的问题。目标固定能力一般由哈希算法提供。

普通的哈希算法实现没办法适应服务器的动态变化,这个时候会用到一致性哈希算法。其本质上是用到一个环形队列,具体内容以及实现参考此链接

2.2.4 整体开销

整体开销为算法整体上的时间和空间开销。

  • 轮询和随机,只需要通过加/减/取模/取随机数等操作,即可实现,开销极小。
  • 哈希,直接用字节流换算成数字取模,开销也很小。
  • 最少连接数,需要保存连接的状态(源IP+目IP+源端口+目端口),产生了部分空间开销,整体开销中等。
  • 最短响应时间,需要定期探测服务器的响应时间,并保存相应数据,开销中等。
  • 最佳处理能力,可理解为最短响应时间的加强版本,需要综合多种数值进行判断,开销较大。

2.2.5 适用协议字段或值

适用协议字段或值为算法可以使用的字段或需要的输入。

  • 轮询和随机,不依赖于任何字段,只要有服务器列表,就可以实现。
  • 哈希,可以用到“源MAC地址”、“目MAC地址”、“源IP地址”、“目IP地址”、“源端口”、“目标端口”这些字段,以及这些字段的自定义组合。
  • 最少连接数,需要保存连接的状态,必须要用到 源IP+目IP+源端口+目端口这4项的组合。
  • 最短响应时间,需要定期向服务器发送请求探测响应时间,可以利用ICMP协议实现。
  • 最佳处理能力,需要定期向服务器发送请求获取性能数据,可以利用SNMP协议实现。

2.3 配套网络设计

区分流量字段和负载均衡算法选定后,便需要进行配套的网络设计。网络设计主要要注意以下两点:

  • 保证数据的来回一致性。
  • 保证高可用性与可拓展性。

2.3.1 保证数据的来回一致性

如何保证数据的来回一致性,其实是保证来回地址一致。数据还没进入到操作系统内核前,保证其不被网卡丢弃

  • 使用二层地址进行负载均衡。客户机发包为AMAC->BMAC,则服务器回包要尽量满足BMAC->AMAC,此点仅在局域网内生效。
  • 使用三层地址进行负载均衡。客户机发包为AIP->BIP,则收到的响应数据包一定是 BIP ->AIP.

关于这个来回一致性笔者想以LVS(一种负载均衡软件,部署在代理上)的三种网络模式进行举例说明,这三种模式实际上对应了三种有代理情况下的网络设计。

NAT模式

NAT(Network Address Translation)模式下,所有数据包都通过代理来修改IP地址字段,客户机发包、服务器回包都需要经过代理。

TUN模式

TUN(Tunnel)模式下,所有数据包都通过代理来修改IP地址字段,客户机发包需要经过代理,服务器回包则不需要经过代理。此模式会在原本数据报外面嵌套新的二层首部和三层首部,实际上这两层首部只是耗材,在抵达服务器网卡后就会被剥离,然后原来的完整数据包可以顺利通过网卡,进入操作系统内核后,进行程序加工处理。

DR模式

DR(Direct Route)模式下,代理可修改IP地址,服务器也通过编程具有了修改IP地址的能力,客户机发包需要经过代理,服务器回包则不需要经过代理。

注:“通过代理来修改IP地址”和“修改IP地址的能力”代表着除了转置以外的修改IP地址能力,转置为对换源目IP地址。一般服务器或主机不经额外编程只有转置的能力,特殊的代理服务器才会有各种各样的修改IP、嵌套封装操作。

上面3种设计都保证了客户机发包和收包的包中的IP是一致的,只有源目IP地址发生了转置。同上面的3种网络设计一样,我们也可以根据我们选定出的算法和字段,在保证数据的来回一致性的情况下,设计出特定的网络设计

2.3.2 保证高可用性与可拓展性

实际上,上面举例的三种模式,就有着对高可用的思考。LVS的三种网络模式中,

  • NAT模式,压力全部在代理上,由代理负责客户机发包和服务器回包的网络转化。
  • TUN模式,在NAT基础上,由直接修改原始首部变为隧道封装,去掉了回包经过代理的过程,代理总体压力减小。
  • DR模式,在NAT基础上,减去了回包经过代理的过程,将开销转移到了服务器上,代理压力最小。

还有一些零碎的点,仅供参考:1. 减少单节点使用代理,尽量多节点冗余。 2.关键节点使用硬件代理产品,比如F5,A10,Citrix的产品。3.使用硬件+软件的多层级负载均衡,硬件只负责四层以下转发,软件负责七层转发。

3 各种负载均衡设计

狭义的负载均衡仅为客户机访问服务器时的流量分发,广义的负载均衡为资源的统筹调度。这个资源可以是线路带宽资源,可以是CPU资源,可以是内存资源,还可以是硬盘资源。负载均衡就是去合理安排这些资源。以下是一些例子,也可以算是负载均衡的一部分。

3.1 使用DNS的负载均衡

客户机访问服务器的时候,还可能会存在除了客户机侧和服务器侧外的第三方乃至第四方。如果使用HTTP,本文1.1-1.4节中都可以引入DNS这样的第三方;如果使用HTTPS,还会引入CA认证机构这样的第四方。CA与认证有关,此处不展开,本处仅在1:m架构的基础上增加DNS侧用于举例。

在上图中,客户机如果要以HTTP形式访问服务器,是先以域名的形式发送到DNS服务器进行解析,解析出域名对应的IP地址后再发起HTTP访问的。利用这点,可以使用DNS服务器做负载均衡功能,不管是私网DNS服务器还是公网DNS服务器。主要有下面两种方式:

  • 不同DNS服务器解析出来的IP不同。比如上海的用户,使用本地DNS,解析出来“ www.test.com ” 的ip是10.1.1.16;山东的用户,使用本地DNS,解析出来是29.1.1.16;这样就将不同省市的流量导向了不同服务器。
  • 同一DNS服务器不同用户解析出来的IP不同。比如访问“ www.test.com ”,现在DNS服务里这个地址下有10个IP,则给不同的用户返回不同的IP,这样就将不同用户的流量导向了不同服务器。

3.2 数据库负载均衡

分库分表,数据库层面上的负载均衡。

3.3 用户负载均衡

应用内也有部分负载均衡途径。如图,网游玩家应该都知道网游会分大区或者服务器。毫无疑问这是用户上的负载均衡。

有的游戏还会分国区/国际区,IOS平台/安卓平台呢,用户负载均衡无疑。

3.4 日常生活中的负载均衡

日常生活中,不同的行政区会使用不同的粤核酸小程序,也算是很好的例子了。什么高并发大流量服务器横向扩容,听起来就花里胡哨,还是得粤核酸1-6来教大家什么是返璞归真的负载均衡高可用。

3.5 线路负载均衡

线路负载均衡一般都是三层路由层面的,这里说个二层的,链路聚合,非网络相关技术人员不需要太关注这节。简单来说,链路聚合就是将多条物理线路在逻辑上视为一条逻辑线路,它的变种“跨设备链路聚合”,则在此之上另外结合了堆叠技术。VPC,MLAG,CLAG,MC-LAG是常见的跨设备链路聚合技术,“跨设备”指的是跨物理设备,但所跨的物理设备是要用堆叠技术形成一台逻辑设备。如下图中,一台接入交换机两条上连线分别接两台汇聚交换机,然后在这两条线画个圈,就代表跨设备链路聚合,这样聚合的多条线也可以看成一条线。

两条物理线路经过链路聚合后,流量较少时(占用带宽<单线的带宽),并不会只跑在一条线工作,而是两条线上负载均衡工作,这个负载均衡利用的就是“源MAC地址+哈希算法”,将不同源MAC地址的数据报分配到不同的线路上以保证二层的有序性。

注: 三层首部中有标识可以分出先后顺序,但二层首部中是分不出先后顺序的,同源MAC地址的两个数据包不能在链路聚合组中的两条物理线路分别发。

3.6 下次想到补充

K8S调度POD到不同节点应该也算负载均衡。

4. 小结

综上所述,欢迎补充。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 客户机和服务器的架构
    • 1.1 单客户机单服务器(1:1)
      • 1.2 多客户机单服务器(n:1)
        • 1.3 单客户机多服务器(1:m)
          • 1.4 多客户机多服务器(n:m)
          • 2. 负载均衡基础
            • 2.1 区分流量字段
              • 2.2 负载均衡算法
                • 2.2.1 代理必须
                • 2.2.2 算法加权
                • 2.2.3 目标固定
                • 2.2.4 整体开销
                • 2.2.5 适用协议字段或值
              • 2.3 配套网络设计
                • 2.3.1 保证数据的来回一致性
                • 2.3.2 保证高可用性与可拓展性
            • 3 各种负载均衡设计
              • 3.1 使用DNS的负载均衡
                • 3.2 数据库负载均衡
                  • 3.3 用户负载均衡
                    • 3.4 日常生活中的负载均衡
                      • 3.5 线路负载均衡
                        • 3.6 下次想到补充
                        • 4. 小结
                        相关产品与服务
                        负载均衡
                        负载均衡(Cloud Load Balancer,CLB)提供安全快捷的流量分发服务,访问流量经由 CLB 可以自动分配到云中的多台后端服务器上,扩展系统的服务能力并消除单点故障。负载均衡支持亿级连接和千万级并发,可轻松应对大流量访问,满足业务需求。
                        领券
                        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档