负载均衡
均衡,存乎于万物之间 -- 阿卡丽
一、概述
1、集群的出现
随着服务用户数量、数据量的不断增长,单体服务已无法承担数据访问的压力。因此,需要考虑服务的扩展,一般来说,服务扩展分为两种:纵向扩展、横向扩展。
纵向扩展是指提升单体服务器性能,比如替换高性能cpu、增大内存、增大网卡带宽等,这种方式好处是操作简单、稳定性强,但是坏处是随着设备性能的提升,继续提升时需要的成本会越来越高,而且单体服务的性能无法无限的提升,总是会达到某个瓶颈。
还有第二种方式,横向扩展是指不提升单个服务性能,而是增加相同性能服务器的数量,由一台服务器增加为两台服务器,这样理论上就会有两倍的性能提升,而且对外像是只有一台服务器一样工作,这种方案的好处是成本比较低,但是坏处是多个服务共同对外提供服务就要求更高的协作性,服务之间的管理更困难。
因此,就有了集群的出现。
2、集群的概念
-
集群(cluster)技术是一种可以在付出较低成本的情况下获得性能、可靠性、可用性等方面相对较高收益的技术,其任务调度则是集群系统中的核心技术。
-
集群是一组相互独立、通过高速网络互连的计算机,他们组成一个组,通常像一个服务器一样对外提供服务。
-
集群可以分为高可用集群、高性能集群。
3、负载均衡的概念
集群是由多个服务器组成的,如果n个服务器只有部分甚至一个服务器工作,或者每个服务器的压力总是不一样的,这样就无法达到缓解单台服务器压力的目的。
因此,我们需要一定的算法将客户端请求的压力大致平均的分散到集群内各个服务器上。
二、网络基础
1、IP地址
1.1 分类

区间:
-
A类:(1.0.0.0 - 126.0.0.0)默认子网掩码:255.0.0.0
-
B类:(128.0.0.0 - 191.255.0.0)默认子网掩码:255.255.0.0
-
C类:(192.0.0.0 - 223.255.255.0)子网掩码:255.255.255.0
-
D类:多播地址
-
E类:保留地址
1.2 私有地址
在IP地址3种主要类型里,各保留了3个区域作为私有地址,其地址范围如下:
-
A类:10.0.0.0 ~ 10.255.255.255
-
B类:172.16.0.0 ~ 172.31.255.255
-
C类:192.168.0.0 ~ 192.168.255.255
2、OSI七层模型
2.1 定义
OSI(Open System Interconnect),即开放式系统互联。 一般都叫OSI参考模型,是ISO(国际标准化组织)组织在1985年研究的网络互连模型。
2.2 划分
OSI定义了网络互连的七层框架(物理层、数据链路层、网络层、传输层、会话层、表示层、应用层),即ISO开放互连系统参考模型。如下图:

2.3 格式
数据也叫做负载,加上TCP首部叫做段,再加上IP首部叫做包,再加上以太网首部叫做帧。

2.4 抓包

3、TCP/IP协议
3.1 基于连接
三次握手四次挥手
3.2 拥塞控制
慢启动、滑动窗口、拥塞窗口等
3.3 可靠传输
对于可靠传输,判断丢包,误码靠的是TCP的段编号以及确认号。
3.4 双工通信
两端可以同时向对端发送数据
4、NAT网关
NAT英文全称是“Network Address Translation”,中文意思是“网络地址转换”。顾名思义,它是一种把内部私有网络地址(IP地址)翻译成合法网络IP地址的技术。我们知道,IPv4地址最多有2的32次方=42亿个,且一部分留用了私网地址,差不多公网地址只有30亿个,去年最后一个公网IPv4地址已经被用了,NAT是一个很好的解决方案。
4.1 特点
1> 网络被分为公网和私网两部分,NAT网关设置在私网到公网的路由出口位置,双向流量都必须经过NAT网关;
2> 网络只能由私网发起,公网无法直接访问私网;
3> NAT网关在两个方向上完成地址的转换和翻译,出方向做源地址转换,入方向做目的地址转换;
4> NAT网关的存在对通信双方都保持透明;
5> NAT网关为了实现双向翻译的功能,需要维护一张关联表,把会话的信息保存下来;
4.2 分类
NAT 从表面上看有三种类型:静态 NAT 、动态地址 NAT 、地址端口转换 NAPT 。
1> 静态NAT
静态NAT适合机器很少的私网环境,通过一个内网地址对应一个外网地址的形式对外映射,这个方式对于解决ip地址不够的问题没有什么帮助。
2> 动态NAT
在静态NAT的基础上,增加外网地址池,在内网机器发送请求的时候,从外网地址池选择一个空闲的外网地址进行映射,而无需配置路由器。
3> 端口转换NAPT
这是最常用的NAT类型。NAPT实际也是动态NAT,只是他是利用多个端口将多个内网地址映射到外网,从而可以用一个外网地址实现多个内网地址的映射。
它包含两种转换方式:SNAT和DNAT
-
SNAT(源NAT):修改数据包的源地址。
-
DNAT(目的NAT):修改数据包的目的地址。
从实现的技术角度,又可以将NAT 分成如下几类:全锥NAT(Full Cone NAT) 、受限锥型NAT (Restricted Cone NAT )、端口受限锥型NAT( Port Restricted Cone NAT) 、对称NAT ( Symmetric NAT) 。
1> 全锥型NAT
全锥NAT把所有来自相同内部IP 地址和端口的请求映射到相同的外部IP 地址和端口。除此之外,外部主机均可随时通过映射的外部IP地址和端口向内部主机发送报文。这种方式,外部请求无任何限制,因此这种方式足够简单但不安全。

2> 受限锥型NAT
受限锥型NAT把所有来自相同内部IP 地址和端口的请求映射到相同的外部IP 地址和端口。但是只有当内部主机曾经发送过报文给外部主机后,外部主机才可以向内部主机发送报文。

3> 端口受限锥型NAT
端口受限锥型NAT 与限制性锥NAT 类似, 只是多了端口号的限制, 即只有内部主机先向外部地址:端口号对发送数据包, 该外部主机才能使用特定的端口号向内部主机发送数据包。

4> 对称NAT
在锥型NAT中,内部主机与外部服务映射关系与外部服务无关,而对称NAT中,只有源IP地址和端口、目的IP地址和端口都相同时,才映射到同一个外网地址及端口上。而且外部主机只有接收到了内部主机的主动请求时,才能向内部主机发送报文。

4.3 穿透
P2P穿透,又叫网络打洞,即使两个内网主机直接进行通信的技术。我们知道,内网主机要想访问外网需要借助NAT,但是外网想访问内网主机会直接被NAT拒绝,除非使用了完全锥型NAT(不安全,用的较少)。因此,想要这两个内网机器进行通信,就需要网络打通。
4.3.1 UDP打洞
-
端点在不同的NAT之下
两个客户端client A 与client B分别处于不同的内网,client A想与client B进行通信的话,需要向client B发送请求包,然后请求包到达NAT B的时候,由于NAT B没有收到过从client B向client A主动发送的包,所以会拒绝这个请求包导致无法进行通信,此时就需要一个服务器Server引导打通。
因为server会与client A和client B保持一个会话,当client A想向client B发送数据包时,先向server发送一个请求,告诉server我要向client B发送数据包了,此时server会向client B发送client A的地址,并让client B也向client A发送请求,此时client A的数据包到达NAT B时,已经有数据包从client B向client A发送过了,所以会直接将数据包转发到client B上,然后client A和client B就能继续完成数据交互了,而且不再需要借助引导服务器server了。
一旦建立了P2P连接,那么client A和client B就能返过来作为引导服务器了,这样就可以极大减轻了服务器server的负载,所以现在很多公司也在使用P2P技术去偷偷利用客户端的资源进行服务。

-
端点在相同的NAT之下
现在来考虑另一种场景,两个客户端A和B正好在同一个NAT之后,而双方都不知道对方的存在,此时A和B就会向之前那样进行网络打通,但是我们知道内网通信肯定是比经过NAT网关转发效率更高的,所以这里的优化方案就是server给client B发送的地址包括client A的外网地址和内网地址,client A和client B会使用两个地址分别进行数据发送,最先到达的一个地址就当作之后通信的地址,因为内网地址一般比较快,如果在一个内网中,内网地址的请求数据会最先到达。

-
端口固定绑定
UDP打洞的一个先决条件:只有当两个NAT都是锥型NAT时才能工作。因为其维持了一个给定的IP地址、端口的内外网绑定,内网向外网发出的和外网向内网发送的应该都是同一个IP地址和端口,而像对称NAT时,每次内网向外网发送请求都使用了新的端口,此时外网就无法再通过之前的IP地址和端口向内网发送数据。
4.3.2 TCP打洞
TCP打洞与UDP打洞的原理是基本相同的,但是区别在于,使用UDP创建的socket可以发送数据也可以接收数据,而使用TCP创建的socket只能发或只能收,而我们知道创建socket是需要绑定一个端口号的,在TCP时用同一个端口号创建出来的发送socket,就无法接收发送到这个端口的数据。此时需要在创建socket时设置SO_REUSEADDR或SO_REUSEPORT属性,使同一个端口可以在多个socket实例上绑定。
5、ARP协议
ARP协议是 Address Resulotion Protocol (地址解析协议)的缩写。其作用是在以太网环境中,数据的传输所依赖的是MAC地址而不是IP地址,而将已知的IP地址转换为MAC地址的工作就是ARP协议来完成的。
在以太网中,实际传输的是帧,帧里面有目标主机的MAC地址。一个主机与另一个主机的通信必须知道对方的MAC地址,但是我们在网络编程里面学过,创建socket绑定的是IP地址和端口,那这个MAC地址又是怎么获取到的呢?他就是通过地址解析协议获得的,所谓地址解析就是主机在发送帧前先将目标IP地址转换为MAC地址的过程。简单来说,就是主机会先向网络发送ARP广播,询问IP地址为xxx的主机MAC地址是什么?网络内的主机收到广播包之后,检查询问的IP地址是否自己的IP地址,如果是就回复自己的MAC地址。如下图:

网络路由
-
网络路由算法:静态路由协议、动态路由协议
-
动态路由算法:距离矢量路由算法、链路状态路由算法
三、负载均衡
1、方案
1.1 硬件设备
早期,负载均衡需要使用专门的负载均衡硬件设备,这种设备通常比较昂贵。
1.2 HTTP 重定向
HTTP 重定向负载均衡,是一种比较简单的负载均衡技术实现。来自用户的http请求到达负载均衡服务器之后,负载均衡服务器根据某种算法,计算得到一个应用服务器的地址,通过http状态吗302重定向响应,将新的ip地址返回给浏览器,用户浏览器收到重定向响应之后,重新发送请求到真正的应用服务器,以此来实现负载均衡。

缺点:
-
用户完成一次访问,需要进行两次请求,一次请求负载均衡服务器,一次请求应用服务器
-
重定向到真正应用服务器,需要将应用服务器的IP地址暴露给客户端,可能带来安全问题,负载均衡服务器一般不部署应用服务,因此一般仅暴露部分端口,设置比较严格的防火墙权限,安全性会更好一点。
因此,重定向负载均衡一般在实践中很少使用。当然也可以使用301跳转,但是跳转之后就无法更新了。
1.3 DNS 负载均衡
我们知道浏览器或app在访问服务时,一般使用的是域名,http基于tcp实现,tcp必须要有对方ip地址才能建立连接,因此域名需要经过dns域名服务器解析为ip地址,而在dns域名服务器解析域名的时候,根据一定规则将不同用户的请求解析为不同ip地址,以此来实现负载均衡。

优点:
-
dns域名解析之后一般会缓存到本地,一段时间内是不需要再重新解析了,所以不用每次访问多个服务器,性能不会受到影响
缺点:
-
域名解析之后同样会暴露应用服务器地址,存在安全性问题,但是一般使用域名解析时,解析的ip地址是负载均衡服务器,也就是两层负载均衡,因此暴露到外部的还是负载均衡服务器的ip地址,不会存在安全问题。现在的域名服务商一般都支持负载均衡,比如ping
1.4 nginx 反向代理
这个应该是我们最熟悉的负载均衡方案了,http重定向用的比较少,dns一般是运维层面的。
一般我们部署服务,都会在应用服务器之前,增加一组nginx,nginx是一个性能比较高的负载均衡和反向代理服务,请求到达nginx之后,nginx首先查看本地是否有缓存,无缓存的话就会将请求发送至应用服务器,发送到哪个应用服务器,就需要根据请求的用户等一些特定的规则进行负载均衡了。
# 注意,这里的server名字即org.tonny.balance不能带下划线
upstream org.tonny.balance {
server 127.0.0.1:8081 weight=1;
server 127.0.0.1:8082 weight=1;
}
# 不带数据的请求,包括restful风格的请求
location / {
proxy_pass http://org.tonny.balance;
# root html;
# index index.html index.htm;
}
# 静态文件,从这里获取
location ~* \.(css|gif|png|jpeg|ico|svg)$ {
root D:/App/nginx/apache-tomcat-7.0.92_1/webapps/ROOT;
}
# jsp类型的请求,从这里走
location ~ \.jsp$ {
proxy_pass http://org.tonny.balance;
}

优点:
-
七层应用负载的好处,是使得整个网络更智能化,比如图片类的请求转发到特定的图片服务器并可以使用缓存技术,将对文字类的请求可以转发到特定的文字服务器并可以使用压缩技术等,我们使用nginx大多是作为一个api网关在使用。
-
对请求数据和响应数据进行任何有意义的修改,极大的提升了应用系统在网络层的灵活性,比如客户请求的header重写,关键字过滤等。
-
可以有效抵抗syn flood攻击,防止服务器资源耗尽导致宕机。
缺点:
-
反向代理负载均衡是工作在应用层网络协议http上的负载均衡,因此也叫应用层负载均衡,或者七层负载均衡,以tcp协议为例,其工作原理首先是客户端与负载均衡服务器建立tcp连接,然后负载均衡服务器在于应用服务器建立tcp连接,因此,七层负载均衡的网络性能损耗会更多一些。(无法直接与应用服务器建立连接的原因主要是七层负载均衡需要根据请求内容进行路由,但是连接包syn不会带任何请求内容,因此无法与应用服务器建立连接)
-
作为一个应用层协议,http相对来说比较重,效率比较低,所以这种反向代理一般用在小规模的互联网系统上,只有几台或者十几台服务器的规模。而且进入出去的数据包都要经过nginx,加重了负载均衡负担。
1.5 IP负载均衡
反向代理负载均衡工作在应用层,我们知道osi七层网络模型中,应用层之下的负载均衡方法是IP层的负载均衡,IP层是网络模型中的网络层,因此也叫网络层负载均衡。
主要工作原理是,当用户的请求数据包到达负载均衡之后,负载均衡服务器会对网络层数据包的ip地址进行转换,将目的ip地址改为应用服务器地址,然后将包发出去,请求数据就会到达应用服务器。应用服务器处理完数据之后再将响应数据返回给负载均衡服务器,负载均衡服务器将数据包的源ip地址改为自身的ip地址返回给客户端。

这里的关键在于真实WEB服务器相应数据包如何返回给负载均衡服务器,一种是负载均衡服务器在修改目的IP地址的同时修改源地址,将数据包源地址改为自身的IP,即源地址转换(SNAT),另一种方案是将负载均衡服务器同时作为真实物理服务器的网关服务器,这样所有的数据都会到达负载均衡服务器。
优点:
-
ip负载均衡不需要在应用层工作,可以在操作系统内核直接修改ip数据包的地址,因此效率比应用层的负载均衡要高得多。
-
ip负载均衡在收到客户端syn请求的时候,通过配置的负载均衡算法,选择一台应用服务器,并将报文的目的ip地址修改为应用服务器的ip地址,因此tcp三次握手连接是与后台服务器直接建立起来的。
缺点:
-
ip负载均衡仍然有个缺陷,就是无论请求还是响应数据包都要经过负载均衡服务器进行ip地址转换,如果请求的是图片、js文件等比较大的文件时,负载均衡流量就会成为系统瓶颈。
-
从安全的视角上,四层负载均衡与服务器直接建立tcp连接,很容易收到syn flood攻击。syn flood是一种广为人知的DDoS(分布式拒绝服务攻击)的方式之一,其通过利用tcp协议缺陷,发送大量的伪造tcp连接请求,从而使得被攻击方资源耗尽的攻击方式。从技术原理上很容易将垃圾流量转发至后台服务器,而七层设备则可以过滤这些恶意并清洗这些流量,但是要求设备本身具备很强的抗DDoS流量的能力。
例如,Nginx主要使用http_limit_conn和http_limit_req模块来防御。 ngx_http_limit_conn_module 可以限制单个IP的连接数,ngx_http_limit_req_module 可以限制单个IP每秒请求数,通过限制连接数和请求数能相对有效的防御CC攻击。但是,如果nginx之前部署了四层负载均衡,并且四层负载均衡在修改目的ip地址的同时,也修改了源ip地址,那么此时nginx就不能对源ip进行限流了,其他方案还有通过geo和map模块设置白名单等实现限流。
1.6 链路层负载均衡
链路层负载均衡,顾名思义是作用在链路层的负载均衡方案。也就是说,负载均衡服务器不修改数据包的ip地址,而是修改数据链路层的mac地址,在数据链路层实现负载均衡。
应用服务器和负载均衡服务器都使用同一个虚拟ip地址,这样ip路由就不会受到影响,负载均衡服务器接收到数据包会选择一个应用服务器的mac地址返回,此时网卡会根据数据包的mac地址,选择自己的数据包接收,因为数据包ip地址没有改变过,所以应用服务器返回数据包时,直接回给到客户端,而不经过负载均衡服务器了。
应用服务器和负载均衡服务器如何才能使用同一个虚拟ip地址呢?这里需要借助一个叫keepalived的工具。

三角传输模式的链路层负载均衡是目前大型网站所使用的最广的一种负载均衡手段。在linux平台上最好的链路层负载均衡开源产品是LVS(linux virtual server)。
2、负载均衡算法
常用的负载均衡算法有轮询、随机、最少连接、哈希、ip地址散列、url散列等多种
2.1 轮询
-
轮询,即按应用服务器数目平均分配请求的方式,实现多服务器处理同时处理请求的算法。这种算法适合各个服务器处理能力相近,每笔业务复杂度类似的场景,如果某个服务器性能较差,则会引起部分请求积压的问题。
-
加权轮询,即给每个服务器增加权重,处理能力强的机器权重大,则分配的请求就更多。
2.2 随机
即按权重设置随机概率。这种方式随着请求量的增加,调用量会分布越来越靠近权重。
2.3 最少连接
-
最少连接,即选择多个服务器中建立连接数最少的服务器处理请求的算法。
-
加权最少连接,即在最小连接的基础上增加权重设置,避免某些性能差的服务器仍会收到比较多的请求。
2.4 哈希
-
普通哈希
-
一致性哈希,相同参数的请求总是会发送到同一节点,当某个节点宕机时,原本发往该节点的请求,根据虚拟节点算法,会被平均分配到其他节点上,其他节点原有请求不变,不会引起剧烈变动。
2.5 ip地址散列
2.6 url散列
通过管理客户端请求的url散列,将发送至相同url的请求转发至同一服务器的算法。
四、lvs+nginx方案
1、lvs简介
lvs(Linux Virtual Server)即Linux虚拟服务器,是由章文嵩博士主导的开源负载均衡项目,目前lvs已经被集成到Linux内核模块中。该项目在Linux内核中实现了基于IP的数据请求负载均衡调度方案。
lvs三种工作模式:NAT模式、TUN模式、以及DR模式。
2、三种模式
2.1 NAT模式
NAT(Network Address Translation)即网络地址转换,其作用是通过数据报头的修改,使得位于企业内部的私有IP地址也可以访问外网,以及外部用户可以访问位于公司内部的私有IP主机。
在NAT工作模式中,lvs负载均衡调度器可以使用两块网卡配置不同的IP地址,eth0设置为私网IP和内部网络通过交换机互联,eth1设备设为外网IP与外部网络互联。然后通过以下三步完成工作:

第一步,用户通过DNS服务器解析到公司负载均衡服务器的外网IP地址,用户可以通过发个这个IP地址与真实服务器通信,但客户不知道自己访问的仅仅是一个调度器,也不清楚后端真实的服务器在哪里、有多少服务器。
第二步,用户请求lvs负载均衡服务器外网地址,lvs收到请求之后选择一台后端服务器并将数据请求包发往后台服务器,并且在转发之前lvs会修改数据包中的目标地址以及目标端口为后端服务器的IP地址和相应端口。
第三步,后端服务器将响应数据返回给lvs调度器,调度器在收到响应数据包后,将响应数据包的源地址和源端口修改为自己的外网IP及相应端口,修改完成后,由调度器将响应数据包发往终端用户,另外,lvs调度器会维护一张连接表,记录连接请求及转发信息,当同一个连接的下一个数据包发送给调度器时,从该表中可以找到之前的连接记录,并根据记录信息选出相同的真实服务器及端口信息。
2.2 TUN模式
在NAT模式下,由于所有的数据请求和响应数据都会经过lvs调度器转发,如果后端服务器的数量太多的话,调度器就会成为整个集群环境的瓶颈。一般说来,请求包会比响应包小很多,所以TUN模式就是将请求和响应数据分离,让调度器只处理请求数据,而响应数据由后端服务器直接返回给客户端。
由于响应数据不再经过lvs调度器,因此需要lvs调度器与后端服务器共享同一个VIP地址(虚拟IP),使用户感知到的是与同一个IP地址进行交互。
TUN模式,又称为IP隧道(IP tunneling)模式,是将一个IP报文封装在另一个IP报文的技术。IP隧道是静态建立的,隧道两端有唯一的IP地址。

2.3 DR模式
DR模式,又叫直接路由模式。该模式不修改数据包的IP地址,而是通过修改数据包的MAC地址进行数据转发的。
与TUN模式类似,该模式也只承担请求的数据包,而由后端将响应数据包直接发往客户端。与TUN模式不同的是该模式要求lvs调度器与后端服务器在同一个局域网内,且VIP地址(虚拟IP)也需要lvs调度器与后端服务器共享,客户端访问的是VIP地址,回应的数据源地址仍然是VIP地址,因此客户端感知不到后端服务器的存在。
由于多台服务器设置同一个VIP地址,所以直接路由模式要求VIP是对外可见的,客户端需要将请求发往lvs调度器,而所有的后端服务器的VIP地址必须配置在Non-ARP的网络设备上,也就是该网络设备不会向外广播自己的MAC机对应的IP地址,后端服务器的VIP是对外不可见的,只用于接收数据包而已。

3、lvs+nginx
3.1 方案
既然lvs负载均衡这么好,为什么不直接采用lvs+应用服务器的模式进行部署呢?
答案肯定是不行的,因为nginx在upstream中配置的应用服务器,在请求一个应用服务器失败时,会重试其他应用服务器,直到一个服务器请求成功或者所有服务器都失败。而lvs进行负载均衡时,如果一个请求发送到应用服务器失败了,应用服务器会直接将失败信息返回给客户端,而不经过lvs,所以lvs无法对其进行重试等操作。
因此一般经典的负载均衡方案就是lvs+nginx。lvs使用keepalived进行主备切换。
除此之外,nginx还可以做静态文件缓存,nginx还可以做自定义开发,作为转发服务器进行分流、转发、业务切换等,甚至对于流媒体服务来说,还可以做音视频编解码、转码等工作。
3.2 架构图


浙公网安备 33010602011771号