服务自适应降载保护设计
设计目的
- 保证系统不被过量请求拖垮
- 在保证系统稳定的前提下,尽可能提供更高的吞吐量
设计考虑因素
- 如何衡量系统负载
- 是否处于虚机或容器内,需要读取 cgroup 相关负载
- 用 1000m 表示 100%CPU,推荐使用 800m 表示系统高负载
- 尽可能小的 Overhead,不显著增加 RT
- 不考虑服务本身所依赖的 DB 或者缓存系统问题,这类问题通过熔断机制来解决
机制设计
-
计算 CPU 负载时使用滑动平均来降低 CPU 负载抖动带来的不稳定,关于滑动平均见参考资料
- 滑动平均就是取之前连续 N 次值的近似平均,N 取值可以通过超参 beta 来决定
- 当 CPU 负载大于指定值时触发降载保护机制
-
时间窗口机制,用滑动窗口机制来记录之前时间窗口内的 QPS 和 RT(response time)
- 滑动窗口使用 5 秒钟 50 个桶的方式,每个桶保存 100ms 时间内的请求,循环利用,最新的覆盖最老的
- 计算 maxQPS 和 minRT 时需要过滤掉最新的时间没有用完的桶,防止此桶内只有极少数请求,并且 RT 处于低概率的极小值,所以计算 maxQPS 和 minRT 时按照上面的 50 个桶的参数只会算 49 个
-
满足以下所有条件则拒绝该请求
-
当前 CPU 负载超过预设阈值,或者上次拒绝时间到现在不超过 1 秒(冷却期)。冷却期是为了不能让负载刚下来就马上增加压力导致立马又上去的来回抖动
-
averageFlying > max(1, QPS*minRT/1e3)-
averageFlying = MovingAverage(flying)
-
在算 MovingAverage(flying)的时候,超参 beta 默认取值为 0.9,表示计算前十次的平均 flying 值
-
取 flying 值的时候,有三种做法:
- 请求增加后更新一次 averageFlying,见图中橙色曲线
- 请求结束后更新一次 averageFlying,见图中绿色曲线
- 请求增加后更新一次 averageFlying,请求结束后更新一次 averageFlying
我们使用的是第二种,这样可以更好的防止抖动,如图:
-
QPS = maxPass * bucketsPerSecond
- maxPass 表示每个有效桶里的成功的 requests
- bucketsPerSecond 表示每秒有多少个桶
-
1e3 表示 1000 毫秒,minRT 单位也是毫秒,QPS*minRT/1e3 得到的就是平均每个时间点有多少并发请求
-
-
降载的使用
- 已经在 rest 和 zrpc 框架里增加了可选激活配置
- CpuThreshold,如果把值设置为大于 0 的值,则激活该服务的自动降载机制
- 如果请求被 drop,那么错误日志里会有
dropreq关键字
参考资料
项目地址
https://github.com/tal-tech/go-zero
如果觉得文章不错,欢迎 github 点个 star 🤝