【金BUG】MsgMesh高频调用time.Sleep引发高CPU占用问题

原创
04/23 10:05
阅读数 164

阿里QA导读:众所周知,发现产品缺陷是QA日常工作,而定位缺陷则是QA的一种基本技能;是技能,就有高低之分。如果你能够发现别人发现不了的BUG,或者在挖掘BUG的方法策略上更加有效,那就证明你的能力更胜一筹,也更能体现测试工程师的这一核心价值能力。下面我们一起来学习下本章的金bug发现历程~

业务背景

MOSN(SOFA MOSN):蚂蚁基于 Service Mesh 理念,结合蚂蚁内部实际场景,将经典微服务下的中间件、数据层、安全层等能力从应用中剥离出来并下沉至独立的 Sidecar 中,结合 k8s 运维体系提供应用无感知的情况下升级基础设施层的能力。MOSN 已下沉 RPC、MSG、Cache、MVC、限流、加密鉴权和无线网关等等诸多能力

图1 经典微服务架构


图2 Service Mesh微服务架构

Bug描述和影响

缺陷描述:MSG 修复注册任务积压问题的代码通过 for 循环检查任务完成状态,间隔时间依据循环次数从 20ms 增至 1000ms,但错误编码使得 for 循环间隔初始值从 20ms 变为 20ns,从而高频率执行 time.Sleep 产生了大量的 syscall 调用,导致 cpu 占用从 3.2 暴增到 35.9,增长了 10 倍。

这里简要画出段循环检查的流程图:

图3 代码流程图

(1)time.Duration 时间单位为1ns

第 2 行 minWaitTime 为 20ms,第 6 行 delay = minWaitTime.Milliseconds() ,此时 delay 值为 20(注意不是20ms,而是 20ms 间隔包含的毫秒数),第 29 行  time.Sleep(time.Duration(delay)) 则致命,time.Duration 表示一段间隔的纳秒数,time.Duration(delay) 返回 20ns,于是 for 循环初始间隔从 20ms 变为为 20ns,最大间隔从 1000ms 降低为 1us,time.Sleep 频率增大百万倍。

(2)time.Sleep 产生多次 syscall 开销较大

由于 Golang 特殊的调度机制,在 Golang 中一次 time.Sleep 可能会产生7次 syscall,而 syscall 的代价是比较高的,单位时间内百万次的 time.Sleep 放大了 syscalll 消耗。

const (
minWaitTime = 20 * time.Millisecond
maxWaitTime = 1000 * time.Millisecond
)

delay := minWaitTime.Milliseconds()
for {
if w.isDone() {
MsgLogAdaptorInstance.Infof("worker is done, exit")
break
}
if w.isRunning() {
url := w.takeURL()
if len(url) != 0 {
delay = minWaitTime.Milliseconds()
if !w.process(url) {
// if no task was executed, then wait for a while and retry
delay = time.Millisecond.Milliseconds()
}
} else {
delay <<= 1
}
} else {
delay <<= 1
}
if delay > maxWaitTime.Milliseconds() {
delay = maxWaitTime.Milliseconds()
}
time.Sleep(time.Duration(delay))
}

BUG影响:在低业务请求下 MOSN 的 CPU 占用增长10倍,如果此缺陷随 MOSN 发布升级至业务生产环境,MOSN 承担的全部 Mesh 能力会因计算资源耗尽而无法工作,引发大面积故障。

Bug发现手段

MOSN 质量基础建设了 MOSN Nightly 流水线、中间件预演和 MOSN 性能基线等质量设施,测试左移帮助我们在测试阶段及时发现该严重缺陷。

(1)Nightly 发版验证

为了在测试阶段提前验证变更代码上线的兼容性,我们在预发和灰度环境选取了若干应用进行 Nightly 发版镜像的试点升级,相应地,我们也铺设了常态化的线上巡检及异常告警,以便提前发现可能的线上缺陷。Nightly 流水线集成了代码覆盖率、单元测试、线下预演部署、sit预演测试、线上预演验证、上线巡检及告警等流程环节,它会依据最新 code commit 构建新镜像,按照既定环节推进,直到质量卡点全部成功或任意一处质量卡点失败,一次流水线可在 1h 内完成对 MOSN 版本的高效高质量验证。

图4 Nightly 流水线

(2)中间件预演

中间件预演是一套多环境多占站点部署的负责中间件产品集成验收的应用集合,沉淀了众多的中间件的测试场景和自动化用例。中间件预演提供了在架构(LDC、非LDC)、环境(测试、预发、灰度和生产)、版本(新版本、稳定版本)等等维度上的集成验证支持,保证RPC、MSG、Cache、安全、限流等等能力下沉 Mesh 的稳定性。

图5 中间件预演架构图

(3)MOSN性能基线

作为数据面,MOSN 需要高性能地支撑蚂蚁内东西向流量路由转发,于是我们制定了性能规范和搭建了预演压测环境,在持续迭代中不断积累自动化压测场景,例如 RPC 的小包请求、大包请求、超时请求等性能场景,保障每个发布正式版本必须满足压测回归要求。

图6 若干Mosn性能压测场景

解决方案和传承

缺陷代码的修复修正 delay 变量取值,保证 time.Sleep 的合理调用。另外,从质量体系角度出发,可以对代码扫描与评审、测试自动化与完整性、性能测试常态化等进行优化:

(1)代码评审环节下,需要定期地分享典型案例,因为专家也需要成长积累,不断补充代码扫描规则是沉淀专家经验一种手段;

(2)预演应用中持续丰富功能场景,把自动化用例数、使用率、成功率作为质量验收卡点;

(3)性能测试常态化运行,日常地把镜像交付、压测场景执行、报告整理、问题追踪等环节流水线化。


关注阿里巴巴技术质量阅读更多





本文分享自微信公众号 - 阿里巴巴技术质量(AlibabaTechQA)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部