本文作者:何建辉(公众号:org_yijiaoqian)
Load Balancing,即负载均衡,是一种计算机技术,用来在多个计算机(计算机集群)、网络连接、CPU、磁盘驱动器或其他资源中分配负载,以达到最优化资源使用、最大化吞吐率、最小化响应时间,同时避免过载的目的。
简单来说:负载均衡(Load Balance),即是将负载(工作任务、访问请求)进行平衡、分摊到多个操作单元(服务器、组件)上进行指向。是解决高性能、单点故障(高可用)、扩展性(水平伸缩)的终极解决方案。
从我们生活中来看,经常免不了要去一些人多拥挤的地方,比如火车站、电影院、银行等等。无论是买票还是排队入场,这些场所一般都会设置多个服务窗口或者入口的。如果没有人引导的话,大多数的情况下,最近的窗口或者入口会被挤满了人,而那些距离较远的窗口或者入口就宽松很多。
针对上面生活中的情况,实际上很浪费资源,因为如何可以把这些排队的人很好的分散到各个窗口或者入口将会大大缩短排队时间。那么,对于网站或者系统的建设也是一样的,为了提升网站的服务能力,很多网站都采用了集群部署。就像电影院有多个入口一样,这时候就需要一个协调者,来均衡的分配这些用户的请求,可以让用户的均匀的分派到不同的服务器上。
我们先回顾一下 OSI 七层模型:OSI 是一个开放性的通信系统互联参考模型,它是一个定义的非常好的协议规范。OSI 模型有7层结构,每层都可以有几个子层。OSI 的七层从上到下分别是:
在这七层模型中,高层次都是依赖于低层次的。层次越高,使用起来越方便。其中高层(即 7、6、5、4层)定义来应用程序的功能,下面3层(即3、2、1层)主要面向通过网络的端到端的数据流。
计算机网络相关的概念:
TELNET、HTTP、FTP、NFS、SMTP、DNS等属于第七层应用层的概念。
TCP、UDP、SPX等属于第四层传输层的概念。
IP、IPX等属于第三层网络层的概念。
ATM、FDDI等属于第二层数据链路层的概念。
了解来网络协议的七层模型以后,再来看看负载均衡。我们可以很明确的一点是,负载均衡是要在网络中传输做文章的。而要在网络传输过程中,那么这七层就势必绕不开。
所以,根据负载均衡技术实现在 OSI 七层模型的不同层次,是可以给负载均衡分类的。
常见的实现方式中,主要可以在应用层、传输层、网络层和数据传输层做文章。所以,工作在应用层的负载均衡,我们通常称之为七层负载均衡,工作在传输层的我们称之为四层负载均衡。
大致可以分为以下机制,其中最常用的是四层和七层负载均衡:
二层负载均衡
一般是用 虚拟mac地址 方式。负载均衡服务器对外提供一个 VIP(虚IP),集群中不通过的机器采用相同 IP 地址,但是机器的 MAC 地址不一样。当负载均衡服务器接收到请求之后,通过改写报文的目标 MAC 地址的方式将请求转发到目标机器实现负载均衡。
三层负载均衡
一般是用 虚拟IP地址 方式。和二层负载均衡类似,负载均衡服务器对外依然提供一个 VIP(虚IP),但是集群中不同的机器采用不同的 IP 地址。当负载均衡服务器接收请求之后,根据不同的负载均衡算法,通过 IP 将请求转发至不同的真实服务器。
四层负载均衡
用 虚拟ip + port 方式。四层负载均衡工作在 OSI 七层模型的传输层,由于在传输层,只有 TCP/UDP协议,这两种协议中除了包含源 IP、目标 IP 以外,还包含源端口号以及目的端口号。四层负载均衡服务器在接收到客户端请求后,以后通过修改数据包的地址信息(IP + 端口号)将流量转发到应用服务器。
七层负载均衡
用 虚拟的url 或 主机名 方式。七层负载均衡工作在 OSI 七层模型的应用层,应用层协议较多,常用http、radius、dns等。七层负载就可以基于这些协议来负载。这些应用层协议中会包含很多有意义的内容。比如同一个 Web 服务器的负载均衡,除来根据 IP + 端口号 进行负载外,还可以根据七层的 URL、浏览器类别、语言 来决定是否进行负载均衡。
因为负载均衡器通常称为 四层交换机 或 七层交换机 。这里针对 四层 和 七层 两者区别再深入说一下:
技术原理区别
以常见的 TCP 为例,负载均衡设备再接收到第一个来自客户端 SYN 请求时,即通过上述方式选择一个最佳的服务器,并对报文中的目标地址进行修改(改为后端服务器IP),直接转发给该服务器。TCP 的连接建立,即 三次握手是客户端和服务端直接建立的,负载均衡设备只是起到一个类似路由器的转发动作 。在某些部署情况下,为保证服务器回包可以正确返回给负载均衡设备,再转发报文的同时可能还会对报文原来的源地址进行修改。
以常见的 TCP 为例,负载均衡设备如果要根据真正的应用层内容再选择服务服务器,只能先代理最终的服务器和客户端建立连接(三次握手)后,才可能接收到客户端发送的真正应用层内容的报文,然后再根据报文中的特定自带,再加上负载均衡设备设置的服务器选择方式,决定最终选择的内部服务器。负载均衡设在这种情况下,更类似于一个代理服务器 。负载均衡和前端的客户端以及后端的服务器会分别建立 TCP 连接。所以从这个技术原理上来看,七层负载均衡明显对负载均衡设备的要求更高,处理七层的能力也必然会低于四层模式的部署方式。
应用场景区别
七层因为可以代理任意修改和处理用户的请求,所以可以使整个应用更加智能化和安全,代价就是设计和配置会更复杂。所以是否有必要使用七层负载均衡是一个需要权衡的问题。
现在的七层负载均衡,主要还是着重于应用HTTP协议,所以其应用范围主要是众多的网站或者内部信息管理平台等基于 B/S 开发的系统。四层负载均衡则对应其他TCP应用。
市面上有很多开源的负载均衡的工具或软件,基本都是基于前面提到的方案实现的,大多数是工作在第七层和第四层的。Nginx、LVS、HAProxy 是目前使用最广泛的三种负载均衡软件。
LVS:主要用来做四层负载均衡
LVS(linux Virtual Server),也就是 Linux 虚拟服务器,是一个由 章文颂博士 发起的自由软件项目,使用 LVS 技术要达到的目标是:通过LVS提供的负载均衡技术和Linux操作系统实现一个高性能、高可用的服务器集群架构,它具有良好的可靠性、可扩展性和可操作性。从而以低廉的成本实现最优的服务性能。
Nginx:主要用来做七层负载均衡
Nginx,是一个网页服务器,它能发现代理HTTP、HTTPS、SMTP、POP3、IMAP的协议链接,以及一个负载均衡器和一个HTTP缓存。
Nginx现在的负载均衡既支持四层(ngx_stream_core_module模块)、又支持七层(ngx_http_upstream_module模块),但由于LVS在四层负载均衡方面做得知名度实在是太高了,所以Nginx的四层负载均衡用的人不怎么多,网上也很少会有说用Nginx来做四层负载均衡的。
HAProxy:主要用来做七层负载均衡
HAPorxy,是一个使用 C 语言编写的自由开源软件,其提供高可用性、负载均衡,以及基于TCP和HTTP的应用程序代理。
负载均衡服务器在决定将请求转发到具体哪台真实服务器的时候,是通过负载均衡算法来实现的。负载均衡算法,是一个负载均衡服务器的核心。
负载均衡算法可以分为两类:静态负载均衡算法 和 动态负载均衡算法 。
静态负载均衡算法包括:轮询、比率、优先权
动态负载均衡算法包括:最少连接数、最快响应速度、观察方法、预测方法、动态性能分配、动态服务器补充、服务质量、服务类型、规则模式
以上,就是目前实现负载均衡的主流算法,不同的负载均衡服务器会选择不同的算法。
使用软件提供的负载均衡服务和使用硬件提供的负载均衡服务目前都有大规模的应用案例。
软件负载均衡服务一般都运行在标准的 x86 服务器上,成本往往比硬件负载均衡设备更便宜,同时能更好适应目前流行的云端服务。
而硬件负载均衡服务则需要特殊设备支持,性能和安全性方面可能会比软件负载服务更好。
上面了解了软件负载均衡,在这里稍微延伸下硬件负载均衡相关的一些内容。
什么是硬件负载均衡?
硬件负载均衡解决方案是直接在服务器和外部网络间安装负载均衡设备,这种设备我们通常称之为负载均衡器,由于专门的设备完成网络请求转发的任务,独立于操作系统,整体性能高,负载均衡策略多样化,流量管理智能化。
硬件负载均衡的优缺点是什么?
优点:直接连接交换机,处理网络请求能力强,与系统无关,负载性可以强。可以应用于大量设施、适应大访问量、使用简单。缺点:成本高,配置冗余.即使网络请求分发到服务器集群,负载均衡设施却是单点配置;无法有效掌握服务器及应使用状态。
使用的注意事项以及应用的场景?
注意事项,需要注意的是硬件负载均衡技术只专注网络判断,不考虑业务系统与应用使用的情况。有时候系统处理能力已经达到了瓶颈,但是此时网络并没有异常,由于硬件负载均衡并没有察觉到应用服务器的异常,还是让流量继续进入到应用服务器。使用场景,一般应用与PV 几十万甚至百万的互联网应用,一般的软件负载均衡器例如Nignx 处理并发一般在1-2w,不足以支撑如此大的网络请求,所以在其之前通常会防止类似F5这样的硬件负载均衡器帮助控制网络请求。如果一般的互联网企业网络请求数在1w左右的可以考虑使用Nignx(软件负载均衡器)就足以满足当前的业务了。
硬件负载均衡器实现哪些功能?
目前市面上有NetScaler, F5, Radware, Array 等产品,基本实现原理大致相同,我们这里把使用的比较多的 F5作为例子给大家做简单解释,算是窥豹一斑。
多链路负载均衡
关键业务都需要安排和配置多条ISP(网络服务供应商)接入链路以保证网络服务的质量。如果某个ISP停止服务或者服务异常了,那么可以利用另一个ISP替代服务,提高了网络的可用性。不同的ISP有不同自治域,因此需要考虑两种情况:INBOUND 和 OUTBOUND。
防火墙负载均衡
针对大量网络请求的情况单一防火墙的能力就有限了,而且防火墙本身要求数据同进同出,为了解决多防火墙负载均衡的问题,F5提出了防火墙负载均衡的“防火墙三明治"方案 防火墙会对用户会话的双向数据流进行监控,从而确定数据的合法性。如果采取多台防火墙进行负载均衡,有可能会造成同一个用户会话的双向数据在多台防火墙上都进行处理,而单个防火墙上看不到完成用户会话的信息,就会认为数据非法因此抛弃数据。所以在每个防火墙的两端要架设四层交换机,可以在作流量分发的同时,维持用户会话的完整性,使同一用户的会话由一个防火墙来处理。而F5 会协调上述方案的配置和实现,把“交换机”,“防火墙”,“交换机”夹在了一起好像三明治一样。
防火墙“三明治”
服务器负载均衡
可用性
安全性
系统管理
总结:对于高并发,高访问量的互联网应用可以考虑加入硬件负载均衡器作为接入层,协助代理层的软件负载均衡器进行负载均衡的工作。硬件负载均衡器的特点是独立于操作系统,处理大访问量,费用高。从功能上来说支持多链路,多服务器,多防火墙的负载均衡,在可用性和安全性上也有良好的表现。
严格来说,Nginx 仅仅是作为 Nginx Proxy 反向代理使用的,因为这个反向代理功能表现的效果是负载均衡的效果。反向代理可以看下:《Nginx 正向代理与反向代理》
Nginx 负载均衡即支持四层( ngx_stream_core_module 模块),也支持七层( ngx_http_upstream_module 模块),但由于 LVS 在四层负载均衡用的比较多,知名度也很高。所以 Nginx 的四层负载均衡用的人不怎么多。
Nginx 不管是四层还是七层,都是用的同一套 upstream 配置,只是使用这个 upstream 的地方会有差别,所以四层和七层的差别其实对于我们配置来说没有影响,反正写起来基本都是通用的。下面会以七层负载均衡为例进行讲解。
不过有一点需要注意,在使用Nginx作为七层负载均衡器时,如果Nginx上有设置 proxy_cache ,那么如果被访问的资源以及被缓存过,Nginx 就不会再将请求转发给节点,而是直接返回缓存资源。也就是说,在 Nginx 在配置缓存的情况下是有可能不会触发调度的。
实现Nginx负载均衡的组件主要有两个:
负载均衡模块,可以实现网站的负载均衡功能及节点的健康检查
proxy 代理模块,用于把请求转发给服务器节点或 upstream 服务器池
(1)upstream 模块介绍
upstream 模块允许 Nginx 定义一组或多组节点服务器组,使用时可以通过 proxy_pass 代理方式把网站的请求发送到事先定义好的对应的 upstream 组的名字上,具体写法为:
proxy_pass http://server_pools
其中 server_pools 就是一个 upstream 节点服务器组名字。
(2)upstream配置案例
示例1: 基本的upstream配置案例:
upstream server_pools {
# upstream是关键字必须有,后面的server_pools是upstream集群组的名字,可自定义名称,调用时就用这个名字。
server 192.168.1.251:80 weight=5;
server 192.168.1.252:80 weight=10;
server 192.168.1.253:80 weight=15;
# server关键字是固定的,后面可以接域名或IP。如果不指定端口,默认是80端口。weight代表权重,数值越大被分配的请求越多。
}
示例2: 较完整的upstream配置案例:
upstream blog_pools {
server 192.168.0.223; #这行标签和下行是等价的
server 192.168.0.224:80 weight=1 max_fails=1 fail_timeout=10s; #这行标签和上一行是等价的,此行多余的部分就是默认配置,不写也可以。
server 192.168.0.225:80 weight=1 max_fails=2 fail_timeout=20s backup;
# server最后面可以加很多参数,具体参数作用看下文的表格
}
(3)upstream模块参数
upstream web_pools {
server linux.example.com weight=5;
server 127.0.0.1:8080 max_fail=5 fail_timeout=10s;
# 当5次连续检查失败后,间隔10s后重新检测。
server linux.example.com:8080 backup;
# 指定备份服务器。作用:等上面服务器全部不可访问时就向它转发请求。
}
(1)proxy_pass 指令介绍
proxy_pass 指令属于 ngx_http_proxy_module 模块,此模块可以将请求转发到另一台服务器。在实际的反向代理工作中,会通过 location 功能匹配指定的 URL,然后把接收到的符合匹配 URL 的请求通过 proxy_pass 抛给定义好的 upstream 节点池。
(2)proxy_pass 的使用案例
location /web/ {
proxy_pass http://127.0.0.1/abc/;
}
将匹配URL为web的请求抛给http://127.0.0.1/abc/
(3)http_proxy 模块参数
默认的调度算法,按照客户端请求逐一分配到不同的后端服务器,宕机的服务器会自动从节点服务器池中剔除。
upstream server_pools {
server 192.168.1.251;
server 192.168.1.252;
}
...
location / {
proxy_pass http://server_pools;
}
注意:对于服务器 性能不同 的集群,该算法容易引发 资源分配不合理 等问题。
在rr轮询算法的基础上加上权重,权重和用户访问成正比,权重值越大,被转发的请求也就越多。
upstream web_pools {
server linux.example.com weight=5;
server 127.0.0.1:8080 max_fail=5 fail_timeout=10s;
# 当5次连续检查失败后,间隔10s后重新检测。
server linux.example.com:8080 backup;
# 指定备份服务器。作用:等上面服务器全部不可访问时就向它转发请求。
}
加权轮询应用于服务器性能不等的集群中,使资源分配更加合理化。
每个请求按访问IP 的 hash 结果分配,每个访客固定访问一个后端服务器,可解决 session 不共享的问题。然而由于是非一致性Hash算法,所以一旦节点数量发生变化,所有的分配映射关系就都会发生改变,在节点配置不稳定的情况下会无法达到预期的效果。
upstream server_pools {
ip_hash;
server 192.168.1.251;
server 192.168.1.252;
}
Session 不共享是说,假设用户已经登陆过,此时发出的请求被分配到了A服务器,但A服务器突然宕机,用户的请求则会被转发到B服务器,但由于Session不共享,B无法直接读取用户的登陆信息来继续执行其他操作。
根据访问URL的hash结果来分配请求,让每个URL定向到同一个后端服务器。所以它非常适合在节点服务器为缓存服务器的情况下使用,能够大大地提供缓存命中率。然而由于是非一致性Hash算法,所以一旦节点数量发生变化,所有的分配映射关系就都会发生改变,在节点配置不稳定的情况下会无法达到预期的效果。
upstream server_pools {
hash $request_uri;
hash_method crc32;
server 192.168.1.251;
server 192.168.1.252;
}
Nginx 在1.7.2 版本之前并不支持hash算法,如果需要在旧版中使用这种算法,就需要安装第三方的hash模块。配置时只需要在 upstream 配置中增加两行 hash $request_uri 、 hash_method crc32 。
根据后端节点服务器的响应时间来分配请求,响应时间短的优先分配。
upstream server_pools {
server 192.168.1.251;
server 192.168.1.252;
fair;
}
Nginx 本身并不支持fair算法,如果需要使用,就需要安装第三方的 upstream_fair 模块。
根据后端节点服务器的连接数情况来分配请求,连接数少的会被优先分配。
least_conn 可以被用于大量长连接的场景(比如游戏服务器),它可以帮助节点服务器均分连接,使负载尽可能地保持相同。
least_conn 算法很简单,它会先遍历一遍所有的节点,比较它们的连接数,然后选取值最小的哪一个节点。如果有多个节点的连接数都是最小的,那么就对它们采用加权轮询算法。可以结合 weight 权重值使用。
upstream balabala {
least_conn;
server 192.168.1.251;
server 192.168.1.252;
}
consistent_hash 是一个基于一致性 Hash 算法产生的静态调度算法,它是 ip_hash 和 url_hash 的升级版。
consistent_hash 同样会把每个请求按照客户端的IP、请求的URL的Hash结果(参数可选)来分配节点,使得同一个Hash值的请求能够始终分配到同一个节点上,它的独特之处在于,一致性Hash算法为它提供了故障前和一致性保持的效果。也就是说,即使在使用过程中某一个节点宕机了,其他Hash值和对应的节点也还是不会受影响,然后可以保持原来的映射关系;而这一个宕机的节点对应的那些Hash值,会被映射到环上的下一个节点上,达到故障迁移的效果。
配置时和url_hash类似,只需要在upstream配置中加一行consistent_hash $request_uri;即可。同样,这个参数也是可以替换为其他内容的。
upstream balabala {
consistent_hash $request_uri;
server 192.168.1.251;
server 192.168.1.252;
}
在浏览器上输入地址[http://www.test.com](http://www.test.com),实现负载均衡效果(可平均访问到两台服务器)
(1) 准备3台nginx服务器,如下
主机名IP地址说明web01192.168.1.251Nginx web01服务器web02192.168.1.252Nginx web02服务器lb192.168.1.253Nginx 负载均衡服务器
(2) 三台服务器均安装Nginx
Nginx的安装这里就不再说了,网上很多参考。
(3) 配置用于测试的Web服务器
分别在web01 和 web02 进行配置
[root@web01 nginx]# cat conf/nginx.conf
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type Application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 80;
server_name localhost;
location / {
root html/www;
index index.html index.htm;
}
access_log logs/access_www.log main;
}
}
创建测试文件数据
[root@web01 ~]# cd /usr/local/nginx/html/
[root@web01 html]# mkdir www
[root@web01 www]# echo "`hostname` www" > index.html
查看创建的文件内容:
[root@web01 www]# cat index.html
web01 www
之后启动nginx服务。
(4) 配Nginx负载均衡服务器
[root@lb01 nginx]# cat conf/nginx.conf
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
upstream www_server_pools { #这里定义Web服务器池,包含了251,252两个Web节点
server 192.168.1.251:80 weight=1;
server 192.168.1.252:80 weight=1;
}
server { #这里定义代理的负载均衡域名虚拟主机
listen 80;
server_name www.test.com;
location / {
proxy_pass http://www_server_pools; #访问www.test.com,请求发送给www_server_pools里面的节点
}
}
}
(5) 域名解析由于不是真实环境,域名使用www.test.com用作测试,所以www.test.com 的解析只能在hosts文件设置。
mac系统:
sudo vi /etc/hosts
windows系统:
C:WindowsSystem32driversetchosts
在末尾添加:192.168.1.253 www.test.com
打开浏览器访问www.test.com,不断刷新会发现所有请求被负载均衡服务器(192.168.1.253)均分配到web01(192.168.1.251)和web02(192.168.1.252)上,实现了负载均衡的效果。