系统自适应限流设计

设计目的

  • 保证系统不被请求压垮
  • 在保证系统稳定的前提下,尽可能的提供高的吞吐

设计考虑因素

  • 如何衡量系统负载
    • 是否处于虚机或者容器里,需要读取cgroup的相关负载
    • 用1000m表示100%的CPU,使用800m表示系统高负载
  • 尽可能小的Overhead,不显著增加RT
  • 不考虑服务本身所依赖的DB或者缓存系统问题,这类问题通过熔断机制来解决

衡量负载的计算方式

  • 这儿拿容器中的衡量方式来举例
  1. 读取容器的cpu.cfs_quota_us,cpu.cfs_period_us

  2. cfs_quota_us/cfs_period_us 得到该容器可用的最多CPU核数

  3. 从/proc/stat 中拿到cpu的使用情况

  4. 从/proc/%d/cgroup 拿到cgroup的使用情况

  5. 计算方式(cgroup使用率*容器可用核数)/ (系统使用情况 * 系统核数)

参考stackover docker cpu uages

机制设计

  • 计算CPU负载时使用滑动平均来降低CPU负载抖动带来的不稳定,关于滑动平均见参考资料

    • 滑动平均就是取之前连续N次值的近似平均,N取值可以通过超参beta来决定
    • 当CPU负载大于指定值时触发降载满足以下所有条件则拒绝该请求保护机制
  • 时间窗口机制,用滑动窗口机制来记录之前时间窗口内的QPS和RT(response time)

    • 滑动窗口使用5秒钟50个桶的方式,每个桶保存100ms时间内的请求,循环利用,最新的覆盖最老的
    • 计算maxQPS和minRT时需要过滤掉最新的时间没有用完的桶,防止此桶内只有极少数请求,并且RT处于低概率的极小值,所以计算maxQPS和minRT时按照上面的50个桶的参数只会算49个
    1. 当前CPU负载超过预设阈值,或者上次拒绝时间到现在不超过1秒(冷却期)。冷却期是为了不能让负载刚下来就马上增加压力导致立马又上去的来回抖动

    2. averageFlying > max(1, QPS*minRT/1e3)

      • averageFlying = MovingAverage(flying)

      • 在算MovingAverage(flying)的时候,超参beta默认取值为0.9,表示计算前十次的平均flying值

      • 取flying值的时候,有三种做法:

        1. 请求增加后更新一次averageFlying,见图中橙色曲线
        2. 请求结束后更新一次averageFlying,见图中绿色曲线
        3. 请求增加后更新一次averageFlying,请求结束后更新一次averageFlying

        我们使用的是第二种,这样可以更好的防止抖动,如图:

        flying策略对比

      • QPS = maxPass * bucketsPerSecond

        • maxPass表示每个有效桶里的成功的requests
        • bucketsPerSecond表示每秒有多少个桶
      • 1e3表示1000毫秒,minRT单位也是毫秒,QPS*minRT/1e3得到的就是平均每个时间点有多少并发请求

    具体实现

    1. 初始化窗口,窗口的时间可自定义,窗口包含的参数为drop数,pass数,total总数

    1. 校验当前请求是否需要drop
    • 优先cpu的load,再然后根据当前的抖动情况

    • highthru方法 计算抖动

    1. 只有drop需要处理,如果需要drop则需要再窗口参数中将drop+1
    2. 不需要drop则继续执行handler方法

    备注:如果是http的请求,则使用middlerware等方式,入rpc请求,则UnaryServerInterceptor的方式注入

后话

这边主要总结了系统的自适应降载的实现,注意:自适应降载是针对于单个服务的降载,不适用于分布式服务(分布式服务用redis实现的令牌桶来做),自适应降载的目的是更平滑的消峰,是系统的不会被猛增的高流量打爆。开头也说了,要想保护下游的服务,请使用熔断策略

参考资料

暂无评论