云计算 第 12 课 深入负载均衡器及其使用策略
本页目录
上一讲中我们接触负载均衡器,并且通过一个模拟实验进行了基本的体验,这节课我们通过 Azure 来探索一下负载均衡器的工作原理。
学习目标
- 了解负载均衡器内部的基本组件
- 理解可能影响负载分配策略的各种因素
- 理解不同的负载分配策略如何影响应用的服务质量(QoS)
- 使用 Round-Robin 策略来实现一个负载均衡器
- 学会如何观察和分析实例池中的资源使用情况,并基于这些信息实现一个高效的负载分配策略
- 在不丢弃请求的前提下,学会如何监控实例的健康状况,并处理实例挂掉的情况
项目简介
负载均衡器复习
在 web 服务中,负载均衡器是非常重要的,为什么呢?原因有二:
- 提高服务质量(QoS),包括吞吐量及延迟等等
- 高可用性(HA),也就是保证接近 100% 的可用性(用户在不同时间地点都可以访问)
前面的项目中,我们使用 Amazon ELB 和 Azure 的 Load Balancer 服务来提高服务质量以及减小花费和处理实例失败。
在 Horizontal Scaling 的时候,负载均衡是由 load generator 完成的。每次添加新机器,load generator 会维护一个列表,并把不同的请求发送给不同的 data center。而在 Auto Scaling 中,我们则是使用 ELB 来完成负载均衡,也就使得我们可以不借助客户端(这里 load generator 可以看作是客户端的集合),就能快速增加机器数量(之前是需要通知客户端来发送给不同的机器d而)。换句话说,我们可以在不更新客户端程序的前提下,根据需求灵活调整机器数量。
除了分配流量,负载均衡器还需要负责处理实例失败。只要 ELB 背后还有一个健康的实例,Load Generator 就会继续发送请求。而 ELB 本身是通过一段时间给实例发消息来检测该实例是否存活的。
看起来很美,对不对,但实际上不同的负载分配策略对性能也会有极大的影响,这个我们后面接着讨论。
负载分配策略
Amazon 的 ELB 使用 Round-Robin 策略来分配负载,并不考虑服务器当前的状况(即使不同的服务器在不同的区域,也不管,反正对于所有机器一视同仁)
上一次我们发送的请求,每一个所需要的服务器资源(CPU,内存,磁盘 IO,网络 IO) 都是类似的,所以我们可以方便地通过增加或减少机器数量来进行调整。
可是实际生活中,不同的请求可能需要的资源差异也很大,如果有些请求需要大量的资源怎么办,这个时候 Round-Robin 策略还是不是高效呢?资源的使用率还会不会平衡呢?这又会如何影响整体的服务质量呢?
这一次我们需要自己实现一个负载均衡的策略,同时还要了解如何监控实例的健康状况,确保只发送请求给健康的实例。
最后需要说明的是,这次的项目主要在 Azure 平台上进行,在 AWS 上重复一次可以得到 10% 的加分,但只能使用 Java
镜像列表
Azure
- Data Center,
Standard_A1,https://cc15319619.blob.core.windows.net/system/Microsoft.Compute/Images/vhds/cc15619p22dcv6-osDisk.b0c453f3-f75f-4a2d-bd9c-ae055b830124.vhd - Load Generator,
Standard_D1,https://cc15319619.blob.core.windows.net/system/Microsoft.Compute/Images/vhds/cc15619p22lgv7-osDisk.c0410b8f-821e-4de3-b725-2a834fd10060.vhd - Load Balancer,
Standard_D1,https://cc15319619.blob.core.windows.net/system/Microsoft.Compute/Images/vhds/cc15619p22lbv2-osDisk.1cf68388-ac67-4165-bec0-67341257d50a.vhd
AWS
- Load Generator,
m3.medium,ami-0d4e6067 - Data Center,
m3.medium,ami-6f486605 - Load Balancer,
m3.medium / m3.large,ami-f44c629e - 标签:
Project: 2.2
因为 Azure 的机制,我们需要把镜像先复制到自己的存储账户中,命令如下:
# LG
azure storage blob copy start https://cc15319619.blob.core.windows.net/system/Microsoft.Compute/Images/vhds/cc15619p22lgv7-osDisk.c0410b8f-821e-4de3-b725-2a834fd10060.vhd --dest-account-name wdxstore --dest-account-key Xc6DDXunKq44PQe3Jhey07fRKeFfPqTm8JBU8CFTPNnj4nCzdgXbWEFvAiM+5F16XBSzwgRyLp0c+os24p4W1w== --dest-container p22
# DC
azure storage blob copy start https://cc15319619.blob.core.windows.net/system/Microsoft.Compute/Images/vhds/cc15619p22dcv6-osDisk.b0c453f3-f75f-4a2d-bd9c-ae055b830124.vhd --dest-account-name wdxstore --dest-account-key Xc6DDXunKq44PQe3Jhey07fRKeFfPqTm8JBU8CFTPNnj4nCzdgXbWEFvAiM+5F16XBSzwgRyLp0c+os24p4W1w== --dest-container p22
# LB
azure storage blob copy start https://cc15319619.blob.core.windows.net/system/Microsoft.Compute/Images/vhds/cc15619p22lbv2-osDisk.1cf68388-ac67-4165-bec0-67341257d50a.vhd --dest-account-name wdxstore --dest-account-key Xc6DDXunKq44PQe3Jhey07fRKeFfPqTm8JBU8CFTPNnj4nCzdgXbWEFvAiM+5F16XBSzwgRyLp0c+os24p4W1w== --dest-container p22
负载均衡器策略
负载模式 - 资源竞争
对于每一台 EC2 实例来说,有如下限制:
- 计算能力 - 每个请求的计算复杂度越高,能够处理的请求总数越低
- 内存容量 - 每个请求所需要的内存越多,能够处理的请求总数越低
- 磁盘 IO - 因为每秒中的读写次数是有上限的,每个请求所需要磁盘读写越多,能够处理的请求总数越低
- 网络 I/O - 带宽是有上限的,传输大的数据包会影响到其他数据的传输速度
通常来说,一旦出现资源过载的现象,系统就不得不杀死当前的进程,也就会导致服务质量的下降。那么我们怎么办呢?之前我们的策略是申请更好的实例或者更多的实例,但是随之而来的就是要花更多的钱,即使如此有时候还是不够(有些实例会超载),所以我们需要想办法,让所有的实例一同承受流量冲击
Round Robin
Round Robin 策略很简单,就是轮转着把每个请求分配到不同的实例上去,而不考虑优先级什么的。这个方法的优势在于:超™简单。
这种策略一旦遇到需要特别多资源的请求时,就可能造成某一台实例超载的同时,其他实例都闲着没啥事儿,服务质量降低了不说,钱也没花到刀刃上。
但是当有一个比较大的实例池的时候,所有需要很多资源的请求集中在一个实例上的机会是很小的(但是并不是没有,还是需要具体情况具体分析)
聪明的负载分配策略
相信聪明的同学已经想到可能的解决办法了,我们只需要找到最合适的实例,把请求对应发过去即可,而不是按顺序一个一个发送。那么问题就来了,怎么找到这个『最合适的』呢?
- 我们需要了解针对一个请求,是要竞争哪一类资源
- 我们需要知道每个 data center 的工作情况
- 我们需要根据使用情况来决定下一个请求要发到哪里
于是可以继续问自己,针对上述情况,什么策略是最好的,又需要通过什么样的数据结构来存储这些信息呢?
这个方法一定会比 Round-Robin 好吗?不一定,因为判断『最合适』本身,就需要一定资源,带来一定延迟,除非有很大提高,不然功不抵过呀。
下面是两个可能的策略:
基于请求执行时间的策略
这种策略的难点在于,如何预测一个请求需要执行多长时间。
基于资源使用率的策略
平衡所有 data center 的使用率,维护一个有序列表,把请求发给负载最小的实例。
Round Robin 实战
我们先来实现一个基于 Round-Robin 的负载均衡器并评估它的效率。这里用两种不同的流量模式来进行测试:
- 周末流量包(测试 Round-Robin 调度)
- 工作日流量包(测试自定义调度)
任务列表
- 在 Azure 中启动一台 load balancer 虚拟机
- SSH 到 load balancer 上(用户名
ubuntu, 密码Cloud@123),需要做的内容在/home/ubuntu/Project22/文件夹中 - 了解给出的框架代码
- 可以在
Main.java找到放置虚拟机 DNS 地址的地方 - 在
LoadBalancer.java的 start() 方法中开始写自定义的负载均衡策略
- 可以在
- 补充完整
LoadBalancer.java中的start()方法,使其可以用 round robin 的方式给已连接的实例分配负载,完成之后使用javac *.java来进行编译 - 如果你确定自己的代码是对的,那么开启三个 data center 虚拟机,并在
Main.java中填写的http://[your DC DNS]里填好对应的 DNS 地址 - 使用
./run来执行你的代码 - 可以在浏览器中访问
http://[your load balancer DNS]看到测试 UI - 确定 load balancer 的 round robin 没有问题后,开启一个 load generator 虚拟机
- 提交密码和 andrew id
- 开始 round robin 测试,目标是达到平均每秒 59 的 RPS
- 访问
http://[your DC DNS]:8080/info/cpu来查看 data center 的 CPU 使用率
负载是否均衡?在这个测试中,round robin 是一个好的策略吗?
接下来我们就要进入自定义调度算法的测试了。
备注
- 可以在终端使用提供的 Java/Python 代码来开启所有的虚拟机。但是如果一个虚拟机没能通过健康检查,你需要在 load balancer 的代码中完成虚拟机的启动。
- 注意代码的整洁和模块化。多写点注释,阐述自己的思路
- 只能修改
LoadBalancer.java,Main.java和DataCenterInstance.java - 不能修改
Request.java,RequestHandler.java,Response.java和ResponseBuilder.java
解题步骤
先用之前的代码复制镜像,然后开启 1 个 Load Balancer,1 个 Load Generator 和 3 个 Data Center。然后 SSH 到 Load Balancer 上:ssh ubuntu@lbvmwdx.eastus.cloudapp.azure.com 密码是 Cloud@123。然后我们把项目代码拷贝回本地 scp -r ubuntu@yourdns.eastus.cloudapp.azure.com:~/Project22/* ./
接着大概观察一下代码,主要做两件事情:
- 把 Data Center 的 DNS 填到
Main.java中 - 补充完全
LoadBalancer.java中的start()方法
这里注意以下几个地方:
LoadBalancer类在被创建的时候就把instances的列表传进来了,直接调用即可- 默认是把所有请求都发给第一个 Data Center 的,只需要改一下这里的索引就好。
- 修改完成之后整个文件夹上传回去
scp -r ./* ubuntu@yourdns.eastus.cloudapp.azure.com:~/Project22/ - 提交 dns 的时候,不用前面的
http和最后的/之类的,注意下格式
接着就和前面任务列表中的一致了,这里不赘述。
改进负载均衡器策略
下一个任务是开发一个负载均衡的策略,以便通过 自定调度算法测试。
可以通过 http://[data center virtual machine dns]:8080/info/cpu 来了解 data center 的 CPU 使用情况,能否通过这个方法,来通过测试呢?
任务列表
- 在 Azure 中启动一台 load balancer 虚拟机
- SSH 到 load balancer 上(用户名
ubuntu, 密码Cloud@123),需要做的内容在/home/ubuntu/Project22/文件夹中 - 补充完整
LoadBalancer.java中的start()方法,使其可以通过查询 CPU 使用率的方式给已连接的实例分配负载,完成之后使用javac *.java来进行编译 - 如果你确定自己的代码是对的,那么开启三个 data center 虚拟机,并在
Main.java中填写的http://[your DC DNS]里填好对应的 DNS 地址 - 使用
./run来执行你的代码 - 可以在浏览器中访问
http://[your load balancer DNS]看到测试 UI - 确定 load balancer 的自定义调度算法没有问题后,开启一个 load generator 虚拟机
- 开始 自定义调度算法 测试,目标是达到平均每秒 41 的 RPS
- 访问
http://[your DC DNS]:8080/info/cpu来查看 data center 的 CPU 使用率
负载是否均衡?在这个测试中,你的算法是一个好的策略吗?
疑难杂症
这里我遇到一个问题,就是 RPS 一直徘徊在 21 左右,死活上不去。我觉得可能是以下几个问题:
- Load Generator 发送请求不正常,因为之前强行停止过测试,不知道有没有影响
- Load Balancer 判断的时候花费的时间太长,如果是这样的话需要优化代码
- 发送请求的间隔数目可能太小,导致检查过于频繁
我觉得很可能是第二个,因为总是能看到选择的 Data Center 的 CPU 使用率为 0,估计是 Load Generator 分发不够快所致。不过我们还是一个一个来试一次
- 重启之后发现并没有什么影响,还是处于比较低的 RPS,所以可以先试试看修改间隔数目
- 间隔数目改成 20 之后并没有特别大的改变,所以可以确定是 Load Balancer 的问题
- 经过问同学和排查,发现了问题所在,需要在选择最空闲的机器之后,继续使用 round robin 的策略
然后问题得以解决。
监控 Data Center 的状态
下一步是让我们的 Load Balancer 能够处理某个 Data Center 挂掉的情况。一旦某个虚拟机挂掉,就开启一个新的,用以代替旧的。
可以通过发送 HTTP 请求来查看 Data Center 的状况,只有返回 200 的时候,才认为它是在工作的。
任务列表
- 在 Azure 中启动一台 load balancer 虚拟机
- SSH 到 load balancer 上(用户名
ubuntu, 密码Cloud@123),需要做的内容在/home/ubuntu/Project22/文件夹中 - 实现带有健康检查的负载均衡器,也就是说,能检测出机器挂掉并停止向其发送请求,然后开启一个新的虚拟机,直到它能工作时,就用新的代替旧的。
- 完成之后使用
javac *.java来进行编译 - 如果你确定自己的代码是对的,那么开启三个 data center 虚拟机,并在
Main.java中填写的http://[your DC DNS]里填好对应的 DNS 地址 - 使用
./run来执行你的代码 - 可以在浏览器中访问
http://[your load balancer DNS]看到测试 UI - 确定 load balancer 的自定义调度算法以及健康检查没有问题后,开启一个 load generator 虚拟机
- 开始 自定义调度算法+健康检查 测试,目标是达到平均每秒 41 的 RPS
- 一切完成之后可以进入最后的测试,目标是 30 RPS
所有测试结束之后,关闭除了 load generator 的其他虚拟机,然后上传代码,包括 /home/ubuntu/Project22 下的所有 java 文件和 references 文件。上传完毕之后可以关闭 load generator
提示
可以用 [dns of a virtual machine]/lookup/random 作为健康检查的链接
在 pom.xml 中配置好
<build>
<sourceDirectory></sourceDirectory>
<outputDirectory></outputDirectory>
....
<build>
就可以编译同一层级的文件了,注意要把 run 文件改为
#!/bin/bash
rm *.class
mvn compile && sudo mvn exec:java -Dexec.mainClass="Main"
性能优化考虑:
- 去掉测试用的输出语句,会比较影响性能
- Health Check 测试中会挂掉其中两台 DC,其中间隔 6 分钟左右(第 6 分钟,第 12 分钟),注意可能出现的异常情况
- 一旦发送请求给挂了的机器,因为需要等待 Timeout,所以会占用很多时间,但是其实很难避免发送请求给已挂的机器,除非增加健康检查的间隔,但是增加间隔本身也会
- 每次访问实际上会调用两次
start()函数,注意这里可能会导致一些问题;网络访问有比较多需要处理的异常,注意保证逻辑的一致性 - 选择下一个的时候性能太差,可能的话改动一下,应该一开始的 RPS 有 50 多
- 需要开新线程,
new Thread(new Runnable).start() - 新开的机器各种不稳定,需要等待一段时间再进行查询
AWS Bonus
在 AWS 上完成前面的三个任务,这里不再重复任务描述。
相关文章
- 云计算 第 13 课 缓存2016-02-15
- 云计算 第 11 课 Horizontal Scaling and Advanced Resource Scaling2016-02-01
- 云计算 第 10 课 Parallel Programming using EMR2016-01-25
- 云计算 第 9 课 Sequential Programming2016-01-20