APP下载

基于Nginx服务器的动态负载均衡策略

2020-08-17刘佳祎崔建明

桂林理工大学学报 2020年2期
关键词:负反馈均衡器内存

刘佳祎,崔建明,智 春

(桂林理工大学 a.现代教育技术中心;b.继续教育学院;c.信息科学与工程学院, 广西 桂林 541006)

0 引 言

因特网的快速发展、人们的需求越来越多样化,促使网络技术不断升级和优化,进而导致服务器的并发访问量和负载量呈指数级增加。由于服务器物理内存和CPU处理速度的限制,单一服务器的处理能力和负载能力无法满足日益增长的网络需求。服务器节点一旦发生故障,就无法保证为用户正常地提供服务,甚至整个系统都有可能瘫痪,使得系统的可靠性和稳定性无法得到保障。为避免发生上述现象,提出了服务器集群技术,多台相互独立的服务器组合了一个庞大的服务器集群,可解决大量并发请求所造成的负载过大、请求错误率高、响应速度慢等问题,实现系统的灵活性,进而满足网络需求[1]。

负载均衡技术通过运用相关算法将来自网络中的高并发请求合理地分配给服务器集群中的节点,目前经典的算法有轮询(RR)、 加权轮询(WRR)、 最小连接(LC)、 加权最小连接(WCL)等[2-5]。随着负载均衡技术的广泛应用,国内外众多学者对其进行改进并提出了一些新的算法。Xu等[6]针对原有的轮询算法提出一种解决负载均衡问题的新轮询优化算法,该算法在负载范围和负载方差方面有很大改进。文献[7]中提出一种对负载信息评价的改进策略,来增强算法的局部搜索能力。为充分利用服务器的资源、缩小平均响应时间,孙乔等[8]提出了一种基于软件定义网络的分布式数据库负载均衡算法。

针对多处理器来说,如何提高其性能、降低负载不均衡率成为学者们研究的重要问题。文献[9]利用动态分配的任务中的线程数量来计算配置比率,从而为每个集群生成新的处理元数量。Singh等[10]提出的新方法优点在于既保留了多路径的优点,又尽可能地保持其开销接近单路径路由。针对如何在原有加权轮询策略上更好地提高集群性能这一问题, 文献[11]提出一种基于服务器性能指标的能够动态调整权重的负载均衡策略, 该方案使每个节点都可以分配到适合其负载能力的任务,进而提高集群的性能。

综上所述,负载均衡技术占有重要地位,应用前景广阔。本文针对Nginx服务器内置负载均衡策略的不足,进行优化设计,提出一种基于Nginx服务器的动态负载均衡策略,该策略由3个模块构成,并在算法调度模块中给出了动态负反馈调度算法以及不同于传统算法的度量指标。

1 动态负载均衡策略的模块总体设计

负载均衡器是系统的核心,而负载均衡策略是其关键点。动态负载均衡策略的实现主要由负载采集、算法调度以及健康检查3个模块共同完成,其中算法调度模块的核心算法又称为动态负反馈调度算法。表1为各模块的功能说明,用户浏览器发送HTTP请求时,各个模块工作流程如图1所示。

表1 各模块功能说明Table 1 Functional description of each module

图1 各模块工作流程图Fig.1 Workflow diagram for each module

当Nginx负载均衡器接收到用户浏览器发送的HTTP请求时,会调用算法调度模块。算法调度模块根据采集到的负载信息通过计算找到最优节点,并将其返回给负载均衡器,负载均衡器会将HTTP请求转发给该最优节点。负载采集模块分为负载均衡器上的Server端以及后端节点上的Client端(本文主要介绍Client端),当Client端接收到Server端的采集命令后,开始采集服务器的负载信息,将其告知Server端。健康检查模块周期性地向后端节点发送HTTP健康检查包,根据后端回复包的状态码来判断健康状态,并记录响应时间。服务器接收到HTTP请求后,进行相应的处理,并将处理结果返回给负载均衡器,并由负载均衡器转发给用户浏览器。

2 动态负载均衡策略中各模块设计

2.1 算法调度模块

2.1.1 动态负反馈调度算法设计整体思路 通过分析Nginx内置的轮询round_robin和最小连接least_conn两个算法可知, 算法中初始权重是由运维人员根据经验而人工设置的, 并没有考虑到服务器性能会随时间不断变化, 而least_conn算法虽然引入了连接数来间接地反映节点的负载情况, 但在处理长连接时会出现连接数不断增多, 而服务器却相对空闲的情况。 本文提出动态负反馈调度算法——dnfs_conn算法,引入负反馈机制对节点进行休眠和唤醒操作,进而提高服务器利用率,减少能耗。

对于本文算法,节点的选择是其核心部分,算法调度模块接收到连接请求时,会调用dnfs_conn算法获取后端服务器集群的节点信息, 若当前负载小于自定义负载下阈值wL, 使用动态编程(dynamic programming)的方法快速递归找到休眠负反馈负载最小节点, 并将其放入休眠队列; 若大于负载上阈值wH,则唤醒节点,继续计算当前负载直到负载值在两者之间为止;如果在上、下阈值之间称之为理想负载,不进行处理。通过计算每个节点的负载值将节点排序,进而选出最优节点,算法流程如图2所示。

图2 算法流程Fig.2 Algorithm flowchart

2.1.2 动态负反馈调度算法相关参数 本文算法的相关参数主要由能够决定服务器承载能力的静态负载因子(static load factor)、 由服务器运行状态决定的、能够反映其当前负载情况的动态负载因子(dynamic load factor)以及从历史的性能数据中提取出来的统计类负载因子(statistical load factor)三大类组成。本文选用响应时间和连接数作为统计类负载因子。算法的相关参数说明见表2。

表2 相关负载因子Table 2 Relevant load factor

2.1.3 动态负反馈调度算法的度量指标 1)负反馈机制。 在控制系统中, 假设某一因素使系统趋于一种状态,另外一个因素使系统远离该状态,如果两者中有一种或两种是非线性的影响,就会造成平衡点的出现。为减小节点的负载波动,引用负反馈机制,假设在tn-1时刻节点的平均负载为

(1)

其中,LCPU(tn-1)、LMem(tn-1)分别为测量所得到的CPU与内存的负载; 系数p为衡量不同应用中CPU和内存的影响;k为集群节点数。

在自动控制理论中, 为了使系统能自动适应工作环境、 任务的变化, 采用由牛顿二项式定理推出的公式——开方的反馈方法(自动调节开方)[12]自动改变自身结构、 参数等, 使系统始终保持稳定的工作状态:

(2)

A=(x±y)i,

(3)

其中,X(m)为初始值;Q为开方次数;x是估计值;y是误差值。A值通常接近于X(m), 故最终负载为

LOAD=LOAD(tn)+(LOAD(tn)-LOAD(tn-1))1/Q。

(4)

在现有的负载模型中同时考虑CPU与内存,运用二维向量取模运算。 同时,为衡量不同应用中CPU和内存的不同影响, 引入系数p。 设集群中一个节点j的负载向量为Lj=〈LCPUj,LMemj〉,LCPUj、LMemj分别为节点j测量所得CPU和内存负载,当有k个节点时, 所有节点在时刻tn的平均负载为

(5)

可知,LOAD(ti)可取值范围为(0, 1),p的取值根据系统应用而定。

2)抖动系数。 抖动现象指在负载变化较大时, 频繁地启动/暂停某一个节点引起的现象。 若同一节点多次迁移, 将会严重影响性能。 因此,如何减少抖动现象的发生是当前的首要任务。 本文算法先计算每个节点的实时负载

(6)

为了增强系统稳定性,考虑节点抖动系数k

(7)

其中: Δt为一个时间周期。k越大,稳定性越差, 则加入抖动系数后的负载为

LOADN(tn)=k×LOAD(tn)。

(8)

3)时间跨度。参考文献[13], 预设在0—T的时间段可分为等长的时间间隔,k段间隔的定义为{(t1-t0),(t2-t1),…,(tk-tk-1)}。由此可知,Tn表示的时间跨度是(tn-tn-1)。

4)k段时间内节点的平均CPU利用率

(9)

其中,pCPUTn是CPU在Tn时间段的平均利用率。 按照这种方式节点的内存利用率也可以计算。

5)响应时间TR。 设每个请求均在一台服务器上进行, 则有

TR=sjtj,

(10)

其中,sj是服务器的请求次数,tj是请求j的处理时长。

6)服务器利用率φ。 设连接数为i时其稳态概率为πi,πc+i为最大连接数时的稳态概率, 则

(11)

其中,c为最大连接数。

7)吞吐量δ。吞吐量作为衡量服务器性能的重要指标, 可直接展现整个服务器处理连接请求的能力, 用λ表示服务率, 可表示为

(12)

2.1.4 dnfs_conn动态负反馈调度算法 根据算法的设计思路给出具体实现过程的伪代码如下:

1.输入:所有节点的CPU和内存信息

2.初始化:给每个节点设置特定名字

3.do

4.通过管理节点,对各工作节能点根据式(5)统计计算得到运行平均负载a1

5.IfwL

6.继续执行操作

7.else ifa1

8.将每个节点根据式(8)计算其负反馈负载并升序排列

9.将得到的负反馈负载节点休眠并放入休眠队列,直到满足wL

10.end for

11.else

12.将休眠队列中的节点唤醒,直到wL

13.end if

14.end do

15.输出:最佳节点

2.2 负载采集模块

本文主要介绍客户端Client上的负载采集,主要使用node.js开发,通过os模块获取系统参数,编写utils模块,用于进行数据处理。

2.2.1 静态负载因子采集 由2.1.2节可知,通过计算静态负载因子SLF可以得到服务器的承载能力,因此定义getSLF方法用于采集SLF。通过os模块获取CPU核心数、主频以及内存容量,代码如下:

let getSLF=function (){

let slfCPU=os.cups()[0].speed * os.cups().length;

let slfMEM=os.totalmem();

return {slfCPU,slfMEM}

}

2.2.2 动态负载因子采集 动态负载因子的采集相对静态的复杂一些,当node.js执行shell命令时所花费的时间是无法有效统计的。因此,使用node.js来执行shell脚本文件,以解决上述问题。将采集动态负载因子分为3部分,代码如下:

1)getDLF将整个采集过程调用并接收结果, 代码如下:

let getDLF=function (){

let [dlfCPU,dlfMEM]=[getDlfCPU(),getDlfMEM()];

return {dlfCPU,dlfMEM}

}

2)getDlfCPU通过执行cpu.sh脚本来获取CPU利用率,代码如下:

function getDlfCPU(){

let dlfCPU=child.exeFileSync(’./cpu.sh’).toString();

return dlfCPU;

}

3)getDlfMEM内存占用率计算,代码如下:

function getDlfMEM(){

let slfMEM=os.totalmem();

let dlfMEM=(slfMEM-os.freemem())/slfMEM;

return dlfMEM;}

2.2.3 周期采集负载信息 客户端采集负载信息具有周期性,调用setInterval方法可以在每隔time秒更新一次负载信息,核心代码如下:

let collectTimer=setInterval(()=>{

updateLoadInfo();

},time * 1000);

function updateLoadInfo(){

let [SLF,DLF]=[collect.getSLF(),collect.getDLF()];

({slfCPU,slfMEM}=SLF);({dlfCPU,dlfMEM}=DLF);

L=a-cpu*dlfCPU+a-mem*dlfMEM;

}

2.3 健康检查模块

将由淘宝Tengine团队开发的nginx_upstream_check_module模块添加到nginx的源码目录中用于检测后端节点的健康状况。使用该模块时,只需要在upstream块中添加check指令即可,配置如下:

upstream backend{

dnfs-conn;

server 192.168.201:80;

server 192.168.202:80;

server 192.168.203:80;

check interval=3000 rise=1 fall=5 timeout=1000;

}

3 实验与结果分析

为了测试本文提出的dnfs_conn算法在高并发时负载均衡能力, 在内网环境下选择lawrence livermore national laboratory(LLNL)的数据在CPU 2.5 GHz、 8 G内存、 Ubuntu16.04操作系统的平台上用Apache HTTP服务器进行实验, 测试需要创建5台云主机组成的集群, 以及一个专有网络VPC(Virtual Private Cloud),其中, 1台云主机作为Nginx负载均衡器, 3台云主机作为Web服务器, 1台云主机作为测试机对系统进行测试。 通过不断增加并发连接数来测试连接失败数和平均响应时间,验证本算法的可行性及优越性并与Nginx内置的round_robin、 least_conn这两种算法的结果进行比较。 根据式(5),设系数p为0.6, 上下阈值wH、wL分别为0.7和0.3, 在同一时刻访问服务器站点连接数1 500、 请求数15 000以下且最大持续时间超过15个时隙的情况下完成平均响应时间、 请求失败数以及负载均衡度的对比结果如图3—5。

图3描述了3种算法在同一时刻服务器站点连接数即并发数在1 500以内的变化情况。对于round_robin算法, 连接数达到1 100以后, 平均响应时间瞬间增加后趋于平稳; least_conn算法的平均响应时间在并发数1 300时忽然增加到区间内的最高值; dnfs_conn算法平均响应时间逐渐增加且始终要低于Nginx内置的其他两种算法,以上表明本文提出的算法在处理高并发数的连接时明显优于其他算法。

图3 平均响应时间对比Fig.3 Comparison of average response times

图4展示了3种算法在请求数为15 000以内时失败次数的对比情况。可以看出,在算法执行过程中,当请求数增加到12 000以后,虽然3种算法均出现失败现象,但在相同并发数下发出相同请求数时,dnfs_conn能够成功处理的请求数较其他两种算法有明显的提高, 能够快速响应服务请求, 表明本文所提出的算法要优于Nginx内置的round_robin和least_conn算法。

图4 请求失败数对比Fig.4 Comparison of request failures

针对负载均衡度的对比,采用方差∑(LOADN-LOAD)2的形式, 其中LOADN为LOADN(ti)在周期内计算的平均值,LOAD为LOAD(ti)在周期内计算的平均值。为体现统计性,分别进行5组测试来求算数平均值,比较结果如图5所示。

图5 方差值对比Fig.5 Comparison of square difference

可知,在最大持续时间大于60 min后,3种算法的方差值有明显的增长现象,但无论最大持续时间在15 min还是240 min,round_robin和least_conn两算法的方差值相差不大; dnfs_conn算法由于每次选取节点的抖动比较小,进而使节点负载方差值明显小于前两者。

4 结束语

在分析Nginx服务器的内置负载均衡策略基础上,提出一种带有动态负反馈调度算法——dnfs_conn算法的动态负载均衡策略, 其中dnfs_conn算法将动态、 静态以及统计类等负载因子综合考虑, 并引入负反馈机制对节点进行休眠和唤醒操作, 进而提高服务器利用率, 减少能耗。 针对3种负载均衡策略的调度算法进行模拟实验, 将测试结果进行对比分析后发现本文提出的负载均衡策略比Nginx内置的round_robin和least_conn负载均衡策略有更好的均衡效果。

猜你喜欢

负反馈均衡器内存
心情如曲调般平衡缤纷
基于Kalman滤波的水声混合双向迭代信道均衡算法
采用负电容结构的新型CTLE均衡器设计
全新的虚短虚断概念与两类集成运放之导出
笔记本内存已经在涨价了,但幅度不大,升级扩容无须等待
“春夏秋冬”的内存
负反馈放大电路设计
基于Multisim的负反馈放大电路的仿真与实验分析
船舶非线性横摇运动的负反馈控制算法
内存搭配DDR4、DDR3L还是DDR3?