背景
已经集成的接口自动化测试 Job 在周期性定时运行的时候,会很小概率出现一直卡在那里,原本应该3min就执行完成的任务,偶尔过了半个小时还没结束。(( ̄3 ̄)直觉告诉我这个地方肯定有问题。。。
但由于复现的概率较小,一个月可能出现一次,所以开始并没有什么太大的线索(仅靠一些猜测并不切实际)。
终于!!!在某一个阳光明媚的大好日子,它重现了。
排查过程
赶紧先登录到服务器上看看进程情况,发现此时存在脚本发起的2个 Worker CPU占用100%(由于用的
Pytest + Pytest-xdist
分布式插件执行用例),当时第一反应是死循环了??通过perf 、ps或者 pstack 打印对应的pid, 看到的结果并没有什么太大用,反而误导我以为是代码里面的一个文件锁导致该问题。
网上查询了一通后,发现一个神奇的工具一一 py-spy (更多使用方法见 官网)
安装后使用命令 :选定一个异常的进程,执行命令:
py-spy top --pid 19785
打印结果如下,很明显看出来当前Cpu都卡在check_retry_status
方法上面。查看该代码,发现该方法中存在一个while循环,并且很凑巧的是。。。╮(╯3╰)╭ 里面没有兜底。
1
2
3
4
5
6
7
8
9
10
11# 具体代码如下,过于相信传进来的 retry_flag 一定是1/2, 如果出现不是这两个值的时候,此时这个方法则进入了死循环。。。
def check_retry_status(cls, uid, atk, order_id, retry_flag, devuuid):
max_count = 5
while max_count > 0:
if retry_flag == 2:
retry_flag = functionA(xxx, ...)
max_count -= 1
elif retry_flag == 1:
return
raise Exception("验证充值订单失败")到这个地方,就很明显定位到了问题,在此处加上兜底并且加上对应日志记录functionA(第三方依赖)到底返回的是什么。( 因为首次传入的 retry_flag 也是来自于functionA)
总结
写代码要谨慎。。。特别是结果来自第三方依赖的时候。。。一定要进行兜底!!!
(emm… 兜一下总比不兜强)