centos7 内存使用率计算_一种根据CPU使用率变化而动态调整请求准入的过载保护方法...

9df196eefdf008c66734c5035d42feed.png

过载保护减少钱包压力

过载,是指系统需要处理的任务超过了自身处理能力极限的情况。如果不对过载进行处理而任由其发展,最直接的影响就是会导致系统崩溃,使其实际能处理的有效任务大大低于其极限处理能力,这就是“雪崩”,更严重的会导致“雪崩”在调用链上的连锁传导,致使关联系统连带的大面积服务瘫痪。

而系统总是为用户提供服务实现用户价值的,过载会导致系统为用户提供的服务大打折扣甚至没法提供服务,没办法实现用户价值,大了影响公司收益,小了影响工作绩效,最终都会体现到系统负责人的钱包上。所以,过载保护,其实保护的是自己的钱包。

基于CPU设计过载保护方法

应对过载,往往会采用服务降级的方式,例如砍掉处理流程中没那么重要的步骤,或者使用缓存数据等,还会采用扩容的方式提升系统整体的处理能力以尽快脱离过载状态。不过,在极端情况下,系统可能都没有办法作出降级回应,扩容的速度可能也跟不上过载恶化的节奏。本文讨论的过载保护是极端情况下的错误处理:通过拒绝处理一些任务,给以出错响应,让系统维持接近极限处理能力的状态,不至于一溃千里。

很多过载保护方法,会基于系统服务的QPS以及请求/响应队列诸如此类的指标来设计,配置一些请求量阈值、队列滞留时长限额等参量,超过了就拒绝。这样的方式,往往在一段时间内可以有较好的效果,但是随着时间推移,请求产生模型、系统处理模型都可能发生变化,这些方法就不奏效了。

其实,QPS也好,队列时间也罢,这些都是系统服务的表征现象,根源症结在于系统提供服务所依赖的资源,包括:CPU、内存、IO等。而在绝大部分情况下,CPU资源是最核心的影响因素,因为内存、IO这些资源很大可能最终都会体现到CPU上面来,在冯·诺伊曼体系结构下的计算机系统中,CPU是当之无愧的“C”位。所以,基于究极核心的CPU资源来设计过载保护方法,可以解决绝大部分的问题。

当然了,过载保护是一个大的话题,有很多需要特别考虑的情况,例如接入层可能需要对连接数特别关照,服务优先级的处理等等,这里就不展开了,本文提到的方法实现了一个v0.1.0的初始化版本,暂时没考虑这些问题。

CPU按摩器

给本文将要阐述的方法取了个名字:CPU按摩器。意思是,在过载情况导致CPU繁忙的情况下,给其上个钟,来一把马杀鸡,放松身体,舒缓心情,状态饱满地做牛做马,而不是压力一大就罢工撂挑子不干了。

272adc61e823cc952fbf608e5351b9a5.png

先吹一下这个按摩器的好处:

  1.  按摩手法简单,一目了然,一学就会,当前用go提供了一个实现,两个API架起来就可以了;

  2. 无需专业设备,随时开按,都是语言层面的支持,不需要依赖DB、文件之类的配置,go语言实现启动了一个周期为1秒的定期采集routine,基本没啥开销;

  3. 力度智能调整,按得重了害怕疼痛伤身,按得轻了担心无感乏意,智能化动态调整保满意,会随着CPU使用率的变化情况,动态调整拒绝服务的概率。

好了,闲话少说,下面详细介绍一下按摩器,直接把go实现v0.1.0版本的README挪过来了,源码可以点击文末“阅读原文”查看。后续找时间补一下C++的实现。

按摩器使用方法

分两步:

  1. 调用StartMassagePlan系列API启动按摩计划;

  2. 调用NeedMassage这个API判断是否要拒绝服务。

启动按摩计划

在程序启动的时候调用StartMassagePlan系列API。例如,在Linux环境下:

func main() {    err := cpumassager.StartMassagePlanLinux()    if err != nil {        handleError() //  处理出错的情况,一般打印一下出错信息        os.Exit(1) //  然后退出就好了    }    serve() //  进入服务程序正常处理流程}

StartMassagePlanLinux是Linux环境下用一系列默认参数启动的建议API,有需要调整相关参数的可以使用StartMassagePlan这个API来设定相关参数启动按摩计划,具体参数的说明,可以参照代码中对于massagePlan结构的注释。默认参数的处理,找个时间优化一下。

判断是否拒绝服务

程序启动,接收到请求,开始处理之前,先调用NeedMassage这个API来决定是正常处理该请求还是拒绝为其服务返回过载的错误信息。

func handleARequest() {    if cpumassager.NeedMassage() {        refuse() //  拒绝服务该请求,做一些简单的处理,例如设定回包的错误码,上报过载告警等        return  //  然后直接返回    }    process() //  正常处理该请求}

按摩器工作原理

按摩器分为如下几个部分:

  1.  提供给服务程序调用的API,具体可以参照"使用方法"部分的说明;

  2. 按摩器内部的CPU使用率收集器、记录器和用于判断是否需要拒绝服务的决断器。

按摩器涉及模块的示意图:

e4b9e2f63dbaa081c5f0e1a4f5be8ff7.png

CPU使用率收集器

服务程序启动按摩计划之后,按摩器就会启动一个routine来每隔1秒钟定期收集CPU使用率。收集器以一个接口的形式提供,不同的操作系统对于CPU使用率的取用方法可能会不一样,可以根据具体情况来提供具体的实现。

Linux平台上CPU使用率,读取procfs(进程文件系统)中的"/proc/stat"文件得到当下的CPU时间,取一个时间段前后的差值就可以得到。具体的可以参照htop的LinuxProcessList_scanCPUTime源码:

https://github.com/hishamhm/htop/blob/402e46bb82964366746b86d77eb5afa69c279539/linux/LinuxProcessList.c#L967

CPU使用率收集器示意图:

8a6a3b206b3677dac1426ae73122d17b.png

CPU使用率记录器

记录器用来记录收集器收集到的CPU使用率数据。记录器并不记录所有的CPU使用率数据,而是维护了一系列计数器(当前是以10为步长,维护了0~90共10个计数器,应该是够用了),每个CPU使用率采集周期,都对每个计数器进行调整:

  1. 如果CPU使用率数据>=当前计数器对应的CPU使用率,那么将该计数器加1,最高加到100;

  2. 如果CPU使用率数据

由于每个计数器的范围是[0, 100],这样就维护了最近100个采集周期(也就是最近100秒)的CPU使用率在不同水位的占比情况。例如,如果">=80计数器"的数值是75,那么就表示最近100次采集数据中,有75次CPU使用率不低于80%。维护这样的计数可以避免某[几]次的CPU使用率统计数据可能的误差,用一段时间内的集聚效果来确保获取到最近一段时间的CPU使用率真实水准。

CPU使用率记录器示意图:

d7fa18d574173c545d3c9992efe76144.png

处理方式决断器

处理决断器,用来决定每个请求的处理方式:

  1. 当CPU处在空闲的 轻松 状态时,每个请求都需要正常处理;

  2. 当CPU处在繁忙的 疲累 状态时,需要根据设定参数以动态变化的概率拒绝为相关请求服务。

判断CPU高低负荷

在具体判断CPU的"轻松"和"疲累"状态时,需要在每个CPU使用率采集时间点计算当下的CPU高低负荷,这个是根据启动按摩计划传入的两个参数和CPU记录器的计数器读数计算得到的:

  1. tirenessLevel,疲累等级,这个是和CPU使用率记录器的计数器相匹配的,按摩器在判断CPU状态的时候,会根据该疲累等级获取对应的计数器读数;

  2. tiredRatio,疲累比例,这个需要和疲累等级配合使用,如果CPU使用率记录器中对应疲累等级的计数器读数的占比高于疲累比例,则认为CPU处于 高负荷 中,否则处于 低负荷 中。

切换CPU状态

CPU状态由轻松到疲累状态比较简单,CPU使用率采集器每个定期采集动作都会判断CPU是否高负荷,如果是在低负荷时检测到高负荷,直接将CPU状态由"轻松"转变成"疲累"即可。

由疲累到轻松的切换,相对复杂一些,先说明一个 intensity-按摩力度 的概念,按摩力度是指拒绝服务的概率,以百分比表示,取值范围是[0, 100],例如,50表示以50%的概率拒绝服务。

还需要说明一个 CPU高/低负荷持续时长 的概念,这个会作为CPU疲累程度调整的依据,高/低负荷状态持续时长只有CPU处于疲累时才有意义,该时长是根据oldestTiredTime(最早判断疲累状态的时间)和latestTiredTime(最近判断为疲累状态的时间)和currentCPUsageRecordTime(当前CPU使用率采集时间)计算得到的:

  1. 在疲累状态下检测到当前CPU处于高负荷,依据如下方式计算得到 CPU高负荷持续时长 :currentCPUsageRecordTime - oldestTiredTime;

  2. 在疲累状态下检测到当前CPU处于低负荷,依据如下方式计算得到 CPU低负荷持续时长 :currentCPUsageRecordTime - latestTiredTime;

  3. CPU每次由轻松转变到疲累或者调整按摩力度的时候都会重置oldestTiredTime和latestTiredTime为currentCPUsageRecordTime;

  4. 每次检测到CPU处于高负荷时,会重置latestTiredTime为currentCPUsageRecordTime。

具体做疲累到轻松的切换时,会根据每个采样动作的判断结果,评估CPU高/低负荷持续时长,以此来对CPU的按摩力度做动态提高或降低,在合适的时机(CPU的按摩力度降为0的时候)才能进入轻松状态,调整按摩力度的时候,需要考虑启动按摩计划时传入的相关参数:

  1. initialIntensity,初始化按摩力度,表示当CPU初次进入疲累状态时候拒绝服务的概率;

  2. stepIntensity,按摩力度调整步进值,如果CPU进入疲累状态后持续处于高负荷/低负荷状态的时间超过配置的检查周期,会以此参数来增加/减少实际的按摩力度;

  3. currentIntensity,实际的按摩力度;

  4. checkPeriodInSeconds,调整按摩力度的检查周期,在CPU处在疲累状态下,CPU状态持续时长超过该值,则会调整按摩力度。

切换CPU状态示意图:

fad095dcafc09017cc250e3c25358e5a.png

疲累时拒绝服务

CPU处于疲累状态时,会根据如下几个方式来决定是否需要拒绝服务:

  1. todoTasks,待处理任务数,疲累状态下每调用一次NeedMassage()就会加1;

  2. doneTasks,已完成任务数,疲累状态下每次调用NeedMassage()返回false的情况下就会加1;

  3. requireTasks,需要完成的任务数,用如下公式计算得到:todoTasks * (100 - currentIntensity) / 100;

  4. 如果doneTasks < requireTasks,则需要提供服务,否则拒绝服务。

这种拒绝服务的方式,基于本地的信息作出决断,算法也非常简单,可以在不增加额外依赖的情况下,提供均匀的拒绝概率。配合前面的动态按摩力度,达到了在过载时候动态维护高服务水准的目的。

期待交流

感兴趣的朋友可以点击“阅读原文”浏览代码。这个公众号比较晚,不能留言,有想要交流的朋友,欢迎点击“在看”发表评论。到代码仓里面提issue就更棒了。

好了,就到这里,祝福大家钱包都鼓鼓的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值