负载均衡

概述 文章转自 https://leehao.me/

在分布式系统中,负载均衡(Load Balancing)是一种将任务分派到多个服务端进程的方法。例如,将一个HTTP请求派发到实际的Web服务器中执行的过程就涉及负载均衡的实现。一个HTTP请求到达Web服务器,这中间涉及多个过程,也存在多种不同负载均衡的方法。本文讲述负载均衡的基本原理与派发策略,下图是负载均衡的基本原理图,图中客户端的请求请求经过达负载均衡器(Load Balancer)的分派,被指定的服务器进程进行处理。

负载均衡基本原理

实现负载均衡主要有两个目的。第一个目的是将任务的处理负载均摊到不同的进程,以减少单一进程的负载,以达到处理能力水平扩容的目的。第二个目的则是提高容错能力。我们知道,在线上正式环境中,机器宕机或者进程异常导致服务不可用是常有的现象。在实现负载均衡的系统中,多个服务器进程提供同样的服务,一个进程不可用的情况下,任务会被负载均衡器派发到其他可用的进程,以达到高可用的目的。在多台不同的服务器中部署相同的服务进程,通过负载均衡对外提供服务,这组进程也称为“集群”(cluster)。

负载均衡实现策略

常见的负载均衡实现策略有以下几种:

  • 均匀派发(Even Task Distribution Scheme)
  • 加权派发(Weighted Task Distribution Scheme)
  • 粘滞会话(Sticky Session Scheme)
  • 均匀任务队列派发(Even Size Task Queue Distribution Scheme)
  • 单一队列(Autonomous Queue Scheme)
  • HTTP重定向
  • DNS负载均衡
  • 反向代理
  • IP负载均衡
  • 数据链路层负载均衡

均匀派发(Even Task Distribution Scheme)

均匀派发是实现负载均衡最简单的策略,均衡派发的意思是指任务将均匀地派发到所有的服务器进程。在实现时,可以使用随机派发或者轮流派发(Round Robin)。

均匀派发策略

均匀派发策略假设集群内所有进程具有相同的处理能力,且任务处理用时相同。但实际上,由于进程部署环境的不同,其处理能力一般不同,任务处理时间也不尽相同。因此均匀派发的策略并不能很好地将任务负载均滩到各个进程中。

DNS负载均衡

我们知道,DNS提供域名解析服务,当我们访问某个站点时,实际上首先需要通过该站点域名的DNS服务器来获取指向该域名的IP地址,在这过程中,DNS服务器完成了域名到IP地址的映射。由于这一映射可以是一对多的关系,因此DNS服务器可以充当负载均衡器的作用,DNS服务器在派发IP地址时,正是使用轮流派发的方式来实现的。

加权派发(Weighted Task Distribution Scheme)

加权派发策略在派发任务时,会赋予服务器进程一个权值,即不同的进程会接受不同数量的任务,具体数量为权值确定。

加权派发策略

例如,三个进程的处理任务的能力比率为3:3:2,那么可以赋予这三个进程3:3:2的权值,即每8个任务中,3个发派给第一个进程,3个发派给第二个进程,2个分派给第三个进程。
加权派发策略考虑了进程处理能力的不同,因此更接近实际的应用。可是,加权派发策略也没有考虑任务处理的要求。

粘滞会话(Sticky Session Scheme)

前面两种负载均衡策略并没有考虑任务之间的依赖关系,在实际中,后面的任务处理常常会依赖于前面的任务。例如,对于同一个登录的用户的请求,用户购买的请求依赖于用户登录的请求,如果用户的登录信息保存在进程1中,那么,如果购买请求被分派到进程2或者进程3,那么购买请求将不能正确处理。这种请求间的依赖关系也称为粘滞会话(Sticky Session),负载均衡策略需要考虑粘滞会话的情况。

粘滞会话策略

粘滞会话的派发策略要求属于同一个会话的任务将会被分派到同一个进程中。虽然这可以正确处理任务,但是却带来任务派发不均匀的问题,因为一些会话可能包含更多的任务,一些会话包含更少的任务。
粘滞会话的另一种处理策略是使用数据库或者缓存,将所有会话数据存储到数据库或者缓存中。集群内所有进程都可以通过访问数据库或者缓存来获取会话数据,进程内存都不保存会话数据,这样,负载均衡器便可以使用前面介绍的策略来派发任务。

均匀任务队列派发(Even Size Task Queue Distribution Scheme)

均匀任务队列派发策略跟加权派发策略类似,都考虑了进程的处理能力,不过其实现方式不同。在均匀队列派发策略下,负载均衡器为每个进程都创建一个大小相等的任务队列,这些任务队列包含了对应进程需要处理的任务。任务处理快的进程,其队列也会减少得快,这样负载均衡器会派发更多的任务给这个进程;相应地,任务处理慢的进程,其队列也会减少得慢,这样负载均衡器会派发更少的任务给这个进程。因此,通过这些任务队列,负载均衡器在派发任务时将进程处理任务的能力因素考虑了进去。

均匀任务队列派发策略

单一队列(Autonomous Queue Scheme)

与上面的均匀队列策略一样,单一队列策略也使用了队列来实现负载均衡。不同的是,单一队列策略只使用了一个队列。

单一队列策略

单一队列策略中,实际上并没有负载均衡器的存在。所有的服务器进程从队列中取出任务执行,如果某个进程出现宕机的情况,那么其他进程仍然可以继续执行任务。这样一来,任务队列并不需要知道服务进程的情况,只需要服务进程知道自己的任务队列,并不断执行任务即可。
单一队列策略实际上也考虑到进程的处理能力,进程处理任务得越快,其从队列取出任务的速度也越快。

HTTP重定向

HTTP重定向服务器是一台普通的Web服务器,用户的请求先到达重定向服务器,这台服务器会挑选一台后端服务器的地址(例如使用轮询的方式),并将该地址写入HTTP重定向响应结果中(以响应状态码302返回)返回给用户。用户将根据这个新的地址重新发送请求到选中的服务器上。选中的服务器会处理用户请求,并将结果返回给用户。
HTTP重定向的处理流程如图所示。

HTTP重定向实现负载均衡

通过重定向服务器的处理,用户请求被分配到不同的后端服务器上进行处理,实现了负载均衡的目的。

优点

HTTP重定向负载均衡的方法实现上比较简单。

缺点

  1. 增加了用户的时延,因为访问请求需要进行两次往返
  2. 重定向服务器没有将后端服务器的负载差异考虑进去,有些后端服务器可能在相当繁忙时仍然接收到较多的请求
  3. 重定向服务器的并发处理能力制约着整个系统的并发处理能力
  4. 如果重定向服务器出现故障,站点就会瘫痪

由于存在这些缺点,HTTP重定向通常都会与其他一种或多种负载均衡技术结合使用。

DNS负载均衡

DNS负载均衡的实现原理是在DNS服务器中为同一个主机名配置多个IP地址,在应答DNS查询时,DNS服务器对于每个查询将以DNS记录的IP地址按顺序返回不同的解析结果,将客户端的访问引导到不同的机器上去,使得不同客户端访问不同的服务器,从而达到负载均衡的目的。

DNS实现负载均衡

优点

  1. 由于DNS系统本身是一个分布式系统,相对来说它不存在性能和吞吐能力的限制,故使用DNS来做负载均衡并不需要担心负载均衡服务器的处理能力
  2. 可以根据用户IP进行智能解析,DNS服务器可以在所有可用的A记录中寻找离用户最近的一台服务器为用户提供服务

缺点

  1. DNS服务器并不知道后端服务器的情况,如果后端服务器宕机,而用户的请求继续被分配到这个后端服务器,会导致无法响应用户的请求
  2. DNS服务器依然没有将后端服务器的负载差异考虑进去
  3. DNS服务器存在缓存,当需要更新请求的分配时,新增一个IP或者删除一个IP并不能立即生效

反向代理

反向代理服务器的工作主要是转发HTTP请求,因此它工作在HTTP层,也就是OSI七层结构中的应用层(第七层),所以基于反向代理的负载均衡也称为七层负载均衡。利用反向代理服务器进行负载均衡,如图3所示。

反向代理实现负载均衡

反向代理服务处于后端服务器的前面,由于需要直接受用户的请求,故反向代理服务器需要一个外网IP。而后端服务器并不直接对外提供访问,因此后端服务器并不需要外网IP。反向代理服务器通过内网IP与后端服务器进行通信。图3中,浏览器的请求到达反向代理服务器114.113.200.84,反向代理服务器收到请求后,根据负载均衡算法计算得到一台真实的后端服务器地址10.0.0.1,并将请求转发到这台后端服务器。10.0.0.1处理请求后将响应返回给反向代理服务器,再由反向代理服务器将该响应返回给用户。
常见的反向代理服务器包括Nginx,Apache,Haproxy等。

优点

  1. 部署比较简单,使用常见的反向代理服务器软件即可部署反向代理服务器
  2. 调度策略丰富,例如,可以为不同的后端机器设置不同的分配权重, 这样处理能力高的机器可以分配到较多的任务,达到能者多劳的目的
  3. 反向代理服务器可以让用户在一次会话周期内的所有请求始终分配到一台特定的后端服务器(粘滞会话)

缺点

由于反向代理工作在HTTP层,所有请求都需要经过反向代理服务器的处理,后端服务器的响应结果也必须经过反向代理服务器传送给用户,故这种负载均衡方式要求反向代理的并发处理能力较高。

IP负载均衡

我们已了解前面几种负载均衡的方法,这些负载均衡都是工作在HTTP层,那么,能否在HTTP层以下来实现负载均衡呢?答案是肯定的。事实上,在数据链路层(第二层),网络层(第三层),以及传输层(第四层)都可以实现不同机制的负载均衡。但与HTTP层机制不同,这些负载均衡调度的工作必须由Linux内核来完成,即网络数据包在从内核缓冲区进入用户进程空间前,已完成转发的工作。
在网络层通过修改请求目的地址进行负载均衡的流程如图4所示。

IP负载均衡

用户请求到达负载均衡服务器114.113.200.84,负载均衡服务器在操作系统内核获取网络数据包,根据负载均衡算法计算得到一台真实的后端服务器10.0.0.1,然后将数据包的目地址改为10.0.0.1,不需要用户进程处理。后端服务器10.0.0.1处理完成后,响应数据包返回到负载均衡服务器,负载均衡服务器再将数据包源地址修改为自身的IP地址(114.113.200.84)发送给用户。

优点

IP负载均衡在内核进程完成数据分发,处理性能得到了很大的提高。

缺点

由于所有请求和响应都要经过负载均衡服务器,系统的最大吞吐量仍然受到负载均衡服务器网卡带宽的限制。对于提供下载服务或者视频服务等需要传输大量数据站点,IP负载均衡的方式是难以满足需求的。

数据链路层负载均衡

数据链路层负载均衡通过修改数据帧的MAC地址来实现负载均衡的目的。数据链路层是OSI网络模型的第二层,由于数据链路层负载均衡的方法走的是MAC层的协议,因此需要负载均衡服务器和后端服务器处在同一个二层(同一个广播域)之中。
数据链路层负载均衡的工作流程如图所示:

数据链路层负载均衡

图5所示的数据传输方式又称作三角传输模式,负载均衡数据分发过程中不修改IP址,只修改MAC地址,通过配置后端服务器与负载均衡服务器具有相同的IP地址,从而达到不修改数据包的源IP地址和目的IP地址就可以进行数据分发的目的。由于后端服务器的IP和数据请求目的IP一致,不需要通过负载均衡服务器进行地址转换,可以将响应数据包直接返回给用户浏览器,避免负载均衡服务器网卡带宽成为瓶颈。
图5中,用户请求到达负载均衡服务器114.113.200.84,负载均衡服务器将请求数据的目的MAC地址必为00.0c.29.d2,并不修改数据包目的IP地址,由于后端服务器与负载均衡服务器并有相同的IP地址,因此数据可以正常传输到达00.0c.29.d2对应的服务器,该服务器处理完成后将响应数据直接返回给用户,不需要经过负载均衡服务器。

在Linux平台上最好的数据链路层负载均衡的开源产品是LVS(Linux Virtual Server)。LVS有三种运行模式,分别为NAT模式,TUN模式,DR模式。DR模式正是运行在数据链路层,通过修改数据帧的MAC地址来实现负载均衡的。

优点

  1. 数据链路层负载均衡工作在Linux内核进程,性能很高
  2. 后端服务器的响应不需要再次经过负载均衡服务器,解决了负载均衡服务器网卡流量瓶颈的问题

缺点

可配置性较高,并不支持复杂的调度策略。