你想知道的负载均衡策略

-回复 -浏览
楼主 2019-08-24 07:29:17
举报 只看此人 收藏本贴 楼主

明天就国庆节啦~大家激动不?“年年有今日,岁岁有今朝”,好尴尬,不知道为何就想到了这句话=.=历史总是不断的重复,我觉得抛开新时代的技术、创新这些来看,大部分人经历的大部分事情,不管是稀松平常还是惊涛骇浪的事情,历史上都有人经历过,和轮回的意思有点儿像。

手动转折。

本文是一篇技术帖,看到过一些很有意思的知识点,和大家分享一下,按照惯例,还是以提出问题和给出解决方案的形式,那么,开始吧~

01

有哪些负载均衡的基础技术呢?

负载均衡是网站必不可少的基础技术手段,不但可以实现网站的伸缩性,同时还改善网站的可用性,具体的技术实现也多种多样,从硬件实现到软件实现,从商业产品到开源软件,应有尽有,但是实现负载均衡的基础技术不外以下几种。

1.HTTP重定向负载均衡

利用HTTP重定向协议实现负载均衡。如图1.1所示。


        图1.1 HTTP重定向负载均衡原理

HTTP重定向服务器是一台普通的应用服务器,其唯一的功能就是根据用户的HTTP请求计算一台真实的Web服务器地址,并将该Web服务器地址写入HTTP重定向响应中(响应状态码302)返回给用户浏览器。

在图1.1中,浏览器请求访问域名www.mysite.com,DNS服务器解析得到IP地址是114.100.80.10,即HTTP重定向服务器的IP地址。然后浏览器通过IP地址114.100.80.10访问HTTP重定向负载均衡服务器后,服务器根据某种负载均衡算法计算获得一台实际物理服务器的地址(114.100.80.3),构造一个包含该实际物理地址的重定向响应返回给浏览器,浏览器自动重新请求实际物理服务器的IP地址,完成访问。

这种负载均衡方案的优点是比较简单。缺点是浏览器需要两次请求服务器才能完成一次访问,性能较差;重定向服务器的自身处理能力有可能成为瓶颈,整个集群的伸缩性规模有限;使用HTTP302响应码重定向,有可能使搜索引擎判断为SEO(搜索引擎优化)作弊,降低搜索排名。因此实践中使用这种方案进行负载均衡的案例并不多见。

2.DNS域名解析负载均衡

这是利用DNS处理域名解析请求的同时进行负载均衡处理的一种方案,如图1.2所示。

图1.2 DNS域名解析负载均衡原理

在DNS服务器中配置多个A记录,如:

www.mysite.com  IN  A  114.100.80.1

www.mysite.com  IN  A  114.100.80.2

www.mysite.com  IN  A  114.100.80.3

* A记录又称IP指向,用户可以在此设置子域名并指向到自己的目标主机地址上,从而实现通过域名找到服务器。指向的目标主机地址类型只能使用IP地址。

每次域名解析请求都会根据负载均衡算法计算一个不同的IP地址返回,这样A记录中配置的多个服务器就构成一个集群,并可以实现负载均衡,图1.2中的浏览器请求解析域名www.mysite.comDNS根据A记录和负载均衡算法计算得到一个IP地址114.100.80.3,并返回给浏览器;浏览器根据该IP地址,访问真实物理服务器。

DNS域名解析负载均衡的优点是将负载均衡的工作转交给DNS,省掉了网站管理维护负载均衡服务器的麻烦,同时许多DNS还支持基于地址位置的域名解析,即会将域名解析成距离用户地理最近的一个服务器地址,这样可加速用户访问速度,改善性能。

但是DNS域名解析负载均衡也有缺点,就是目前的DNS是多级解析(操作系统检查host->本地域名服务器->根域名服务器->顶级域名服务器->权限域名服务器),每一级DNS都可能缓存A记录,当下线某台服务器后,及时修改了DNSA记录,要使其生效也需要较长时间,这段时间,DNS依然会将域名解析到已经下线的服务器,导致用户访问失败;而且DNS负载均衡的控制权在域名服务商那里,网站无法对其做更多改善和更强大的管理。

事实上,大型网站总是部分使用DNS域名解析,利用域名解析作为第一级负载均衡手段,即域名解析得到的一组服务器不是实际提供Web服务的物理服务器,而是同样提供负载均衡服务的内部服务器,这组内部负载均衡服务器再进行负载均衡,将请求分发到真实的Web服务器上。(软件世界中,解决问题的方法往往是增加中间层)

3.反向代理负载均衡

利用反向代理服务器进行负载均衡,如图1.3所示。

图1.3 反向代理负载均衡原理

大多数反向代理服务器同时提供负载均衡的功能,管理一组Web服务器,将请求根据负载均衡算法转发到不同的Web服务器上。Web服务器处理完成的响应也需要通过反向代理服务器返回给用户。由于Web服务器不需要使用外部IP地址,而反向代理服务器则需要配置双网卡和内部外部两套IP地址。

图1.3中,浏览器访问请求的地址是反向代理服务器的地址114.110.80.10,反向代理服务器收到请求后,根据负载均衡算法计算得到一台真实物理服务器的地址10.0.0.3,并将请求转发给服务器。10.0.0.3处理完请求后将相应返回给反向代理服务器,反向代理服务器再将该响应返回给用户。

由于反向代理服务器转发请求在HTTP协议层面,因此也叫应用层负载均衡,其优点是和反向代理服务器功能集成在一起,部署简单。缺点是反向代理服务器是所有请求和响应的中转站,其性能可能会成为瓶颈。

4.IP负载均衡

网络层通过修改请求目标地址进行负载均衡,如图1.4所示。

图1.4 IP负载均衡原理

用户请求数据包到达负载均衡服务器114.100.80.10后,负载均衡服务器在操作系统内核进程获取网络数据包,根据负载均衡算法计算得到一台真实Web服务器10.0.0.1,然后将数据目的IP地址修改为10.0.0.1,不需要通过用户进程处理。

真实Web应用服务器处理完成后,响应数据包回到负载均衡服务器,负载均衡服务器再将数据包源地址修改为自身的IP地址(114.100.80.10)发送给用户浏览器(在这里要修改一次源地址,因为真实处理请求的服务器源地址为10.0.0.1,要将其改为负载均衡服务器的地址)。

这里的关键在于真实物理Web服务器响应数据包如何返回给负载均衡服务器。一种方案是负载均衡服务器在修改目的IP地址的同时修改源地址,将数据包源地址设为自身IP,即源地址转换SNAT),这样Web服务器的响应会再回到负载均衡服务器;另一种方案是将负载均衡服务器同时作为真实物理服务器集群的网关服务器,这样所有响应数据都会到达负载均衡服务器。

* 源地址转换即内网地址向外访问时,发起访问的内网IP地址转化为指定的IP地址(可指定具体的服务以及相应的端口或端口范围),这可以使内网中使用保留IP地址的主机访问外部网络,即内网的多部主机可以通过一个有效的公网IP地址访问外部网络。

网关服务器:实质上是一个网络通向其他网络的IP地址。

IP负载均衡在内核进程完成数据分发,较反向代理负载均衡(在应用程序中分发数据)有更好的性能。但是由于所有请求响应都需要经过负载均衡服务器,集群的最大响应数据吞吐量不得不受制于负载均衡服务器网卡带宽。对于提供下载服务或者视频服务等需要传输大量数据的网站而言,难以满足需求。能不能让负载均衡服务器只分发请求,而使响应数据从真实物理服务器直接返回给用户呢?

5.数据链路层负载均衡(又称直接路由方式)

数据链路层负载均衡是指在通信协议的数据链路层修改mac地址进行负载均衡,如图1.5所示。mac地址:MAC地址是固化在网卡上串行EEPROM中的物理地址,通常有48位长。(特意解释mac的梗~)

图1.5 数据链路层负载均衡原理

在图1.5中,用户请求到达负载均衡服务器114.100.80.10后,负载均衡服务器将请求目的mac地址修改为00: 0c: 20: d2,并不修改数据包目标IP地址,由于Web服务器集群所有服务器的虚拟IP地址都和负载均衡服务器IP地址相同,因此数据可以正常传输到达mac地址00: 0c: 20: d2对应的服务器,该服务器处理完成后发送响应数据到网站的网关服务器,网关服务器直接将该数据包发送到用户浏览器,响应数据不需要通过负载均衡服务器。

这种数据传输方式又称作三角传输模式,负载均衡数据分发过程中不修改IP地址,只修改目的mac地址,通过配置真实物理服务器集群所有机器虚拟IP和负载均衡服务器IP地址一致,从而达到不修改数据包的源地址和目的地址就可以进行数据分发,由于实际处理请求的真实物理服务器IP和数据请求目的IP一致,不需要通过负载均衡服务器进行地址转换,可将响应数据包直接返回给用户浏览器,避免负载均衡服务器网卡带宽成为瓶颈。这种负载均衡方式又称作直接路由方式(DR)。

使用三角传输模式的链路层负载均衡是目前大型网站使用最广的一种负载均衡手段。可参考Linux平台上的链路层负载均衡开源产品LVSLinux Virtual Server)。

02

有哪些负载均衡算法?

负载均衡服务器的实现可以分成两个部分:

1.根据负载均衡算法和Web服务器列表计算得到集群中一台Web服务器的地址。

2.将请求数据发送到该地址对应的Web服务器上。

前面描述了如何将请求数据发送到Web服务器,而具体的负载均衡算法通常有以下几种:

1.轮询(Round Robin, RR)

所有请求被依次分发到每台应用服务器上,即每台服务器需要处理的请求数目都相同,适合于所有服务器硬件都相同的场景。

加权轮询(Weighted Round Robin, WRR)

根据应用服务器硬件性能的情况,在轮询的基础上,按照配置的权重将请求分发到每个服务器,高性能的服务器能分配到更多请求。

2.随机(Random)

请求被随机分配到各个应用服务器,在许多场合下,这种方案都很简单实用,因为好的随机数本身就很均衡。即使应用服务器硬件配置不同,也可以使用加权随机算法。

3.最少连接(Least Connections)

记录每个应用服务器正在处理的连接数(请求数),将新到的请求分发到最少连接的服务器上,应该说,这是最符合负载均衡定义的算法。同样,最少连接算法也可以实现加权最少连接。

4.源地址散列(Source Hashing)

根据请求来源的IP地址进行Hash计算,得到应用服务器,这样来自同一个IP地址的请求总是在同一个服务器上处理,该请求的上下文信息可以存储到这台服务上,在一个会话周期内重复使用,从而实现会话黏滞。(大家可以翻之前的技术帖回顾一下,当用这种方式来实现Session绑定时,不是一个好的方案)

03

分布式缓存集群怎么实现伸缩性设计呢?

分布式缓存,不同于应用服务器集群的伸缩性设计,分布式缓存的伸缩性不能使用简单的负载均衡手段来实现。

和所有服务器都部署相同应用的应用服务器集群不同,分布式缓存服务器集群中不同服务器缓存的数据各不相同,缓存访问请求不可以在缓存服务器集群中任意一台处理,必须先找到缓存了需要数据的服务器,然后才能访问。这个特点会严重制约分布式缓存集群的伸缩性设计,因为新上线的缓存服务器没有任何数据,而已下线的缓存服务器还缓存着网站的许多热点数据。

Memcached分布式缓存集群的访问模型

以Memcached为代表的分布式缓存,访问模型如图3.1所示。

图3.1 Memcached分布式缓存访问模型

应用程序通过Memcached客户端访问Memcached服务器集群,Memcached客户端主要由一组APIMemcached服务器集群路由算法、Memcached服务器集群列表及通信模块构成。

一个典型的缓存写操作如图3.1中箭头所示路径。应用程序输入需要写缓存的数据<BEIJING,DATA>,API将KEYBEIJING)输入路由算法模块,路由算法根据KEY和Memcached集群服务器列表计算得到一台服务编号(NODE1),进而得到该机器的IP地址和端口(10.0.0.1:91000)。API调用通信模块和编号为NODE1的服务器通信,将数据<BEIJING,DATA>写入该服务器。完成一次分布式缓存的写操作。

在Memcached分布式缓存系统中,对于服务器集群的管理,路由算法至关重要,和负载均衡算法一样,决定着究竟该访问集群中哪台服务器。

1.余数Hash路由算法

简单的路由算法可以使用余数Hash:用服务器数目除以缓存数据KEYHash值,余数为服务器列表下标编号。假设图3.1BEIJING的Hash值是490806430Java中的HashCode()返回值),用服务器数目3除以该值,余数得到1,对应节点NODE1。(这块地方,作者应该是指490806430%3=1这种求余计算,我也分不清这个“除以”的描述正不正确)由于HashCode具有随机性,因此使用余数Hash路由算法可保证缓存数据在整个Memcached服务器集群中比较均衡的分布。

对余数Hash路由算法稍加改进,就可以实现和负载均衡算法中加权负载均衡一样的加权路由。事实上,如果不需要考虑缓存服务器集群的伸缩性,余数Hash几乎可以满足绝大多数的路由需求。

但是,当分布式缓存集群需要扩容的时候,事情就变得棘手了。假设由于业务发展,网站需要将3台缓存服务器扩容至4台。更改服务器列表,仍旧使用余数Hash,用4除以BEIJING的Hash值490806430,余数为2,对应服务器NODE2。由于数据<BEIJING,DATA>缓存在NODE1,对NODE2的读缓存操作失败,缓存没有命中。

很容易计算出,3台服务器扩容至4台,大约有75%3/4)被缓存的数据不能正确命中,随着服务器集群规模的增大,这个比例直线上升。当100台服务器的集群中加入一台新服务器,不能命中的概率是99%N/(N+1))。

这个结果显然是不能接受的,在网站业务中,大部分的业务数据读操作请求事实上是通过缓存获取的,只有少量读操作请求会访问数据库,因此数据库的负载能力是以有缓存为前提而设计的。当大部分被缓存了的数据因为服务器扩容而不能正确读取时,这些数据访问的压力就落到了数据库身上,这将大大超过数据库的负载能力,严重的可能会导致数据库宕机(这种情况下,不能简单的重启数据库,网站也需要长时间才能逐渐恢复正常,因为数据库一重启,数据访问请求又会再次击穿缓存来到数据库,数据库容易再次宕机。)

一种解决办法是在网站访问量少的时候扩容缓存服务器集群,这时候对数据库的负载冲击最小。然后通过模拟请求的方法逐渐预热缓存,使缓存服务器中的数据重新分布。

那么,能不能通过改进路由算法,使得新加入的服务器不影响大部分缓存数据的正确命中呢?目前比较流行的算法是一致性Hash算法。

2.一致性Hash算法

本人看过很多解释一致性Hash算法的文章,这本书里的描述是唯一一次看懂了的(啊摔……),遂分享之~

一致性Hash算法通过一个叫作一致性Hash环的数据结构实现KEY到缓存服务器的Hash映射,如图3.2所示。

图3.2 一致性Hash算法原理

具体算法过程为:先构造一个长度为0~2³²的整数环(这个环被称作一致性Hash环),根据节点名称的Hash值(其分布范围同样为0~2³²)将缓存服务器节点放置在这个Hash环上。当需要查找缓存的数据时,先根据数据的KEY值计算得到其Hash值(其分布范围也同样为0~2³²),然后在环上顺时针查找距离这个KEYHash值最近的缓存服务器节点,完成KEY到服务器的Hash映射查找。

在图3.2中,假设NODE1Hash值为3,594,963,423NODE2Hash值为1,845,328,979。KEY0Hash值为2,534,256,785,那么KEY0在环上顺时针查找,找到的最近的节点就是NODE1

当缓存服务器集群需要扩容的时候,只需要将新加入的节点名称(NODE3)的Hash值放入一致性Hash环中,由于KEY是顺时针查找距离其最近的节点,因此新加入的节点只会影响整个环中的一小段,如图红色线条画的一段(NODE2-NODE3间的节点)。

图3.3 增加节点后的一致性Hash环结构

假设NODE3的Hash值是2,790,324,235,那么新加入NODE3后,KEY0Hash2,534,256,785)顺时针查找得到的节点就是NODE3

图3.3中,加入新节点NODE3后,原来的KEY大部分还能继续计算到原来的节点,只有KEY3KEY0从原来的NODE1重新计算到NODE33台服务器扩容至4台服务器,可以继续命中原有缓存数据的概率是75%,远高于余数Hash25%,而且随着集群规模越大,继续命中原有缓存数据的概率也逐渐增大,100台服务器扩容增加1台服务器,命中率是99%。虽然仍有小部分数据不能被读到,但是这个比例足够小,通过访问数据库获取也不会对数据库造成致命的负载压力。

具体应用中,这个长度为2³²的一致性Hash环通常使用二叉查找树实现Hash查找过程实际上是在二叉查找树中查找不小于查找数(KEYHash值)的最小数值。这个二叉树的最右边叶子节点和最左边的叶子节点相连接,构成环。

但是,上面描述的算法过程还存在一个小小的问题。

新加入的节点NODE3只影响了原来的节点NODE1,也就是说一部分原来需要访问NODE1的缓存数据现在需要访问NODE3(概率上是50%)。但是原来的节点NODE0NODE2不受影响,这就意味着NODE0NODE2负载压力是NODE1NODE3的两倍(负载不均衡)。如果4台机器的性能是一样的,那么这种结果显然不是我们需要的。

怎么办呢?

计算机的任何问题都可以通过增加一个虚拟层来解决。计算机网络的7层协议,每一层都可以看作是下一层的虚拟层;计算机操作系统可以看作是计算机硬件的虚拟层;Java虚拟机可以看作是操作系统的虚拟层;分层的计算机软件架构事实上也是利用虚拟层的概念。

解决上述一致性Hash算法带来的负载不均衡问题,也可以通过使用虚拟层的手段:将每台物理缓存服务器虚拟为一组虚拟缓存服务器,将虚拟缓存服务器的Hash值放置在Hash环上,KEY在环上先找到虚拟服务器节点,再得到物理服务器的信息。

这样新加入物理服务器节点时,是将一组虚拟节点加入环中,如果虚拟节点的数目足够多,这组虚拟节点将会影响同样多数目的已经在环上存在的虚拟节点,这些已经存在的虚拟节点又对应不同的物理节点。

最终结果是:新加入一台缓存服务器,将会较为均匀的影响原来集群中已经存在的所有服务器,也就是说分摊原有缓存服务器集群中所有服务器的一小部分负载,其总的影响范围和上面讨论过的相同。如图3.4所示。

图3.4 使用虚拟节点的一致性Hash环

在图3.4中,新加入节点NODE3对应的一组虚拟节点为V30V31V32,加入到一致性Hash环后,影响V01V12V22三个虚拟节点(V30V31V32顺时针的下一个节点),而这三个虚拟节点分别对应NODE0NODE1NODE2三个物理节点。

最终Memcached集群中加入一个节点,但是同时影响到集群中已存在的三个物理节点,在理想情况下,每个物理节点受影响的数据量(还在缓存中,但是不能被访问到的数据)为其节点缓存数据量的1/4(x/(N+X))N为原有物理节点数,X为新加入的物理节点数,也就是集群中已经被缓存的数据有75%可以被命中,和未使用虚拟节点的一致性Hash算法结果相同。

显然每个物理节点对应的虚拟节点越多,各个物理节点之间的负载越均衡,新加入物理服务器对原有的物理服务器的影响越保持一致(这就是一致性Hash这个名称的由来)。在实践中,一台物理服务器虚拟为多少个虚拟服务器节点合适呢?太多会影响性能,太少又会导致负载不均衡,书籍作者建议值是150,当然,具体情况具体分析。

写在落尾

下期技术帖打算介绍Java并发编程的知识点,ExecutorFuturecallablerunnablefutureTaskThreadLocal等。

长存敬畏之心,温故而知新,本文内容来自书籍:

李智慧. 大型网站技术架构:核心原理与案例分析. 北京:电子工业出版社,2013



我要推荐
转发到