起因
客户:A工啊,怎么回事啊,这个按钮怎么点了之后老是没有反应啊,最后还和我说什么超时了,怎么回事啊?于是乎开始进行问题的排查。
问题表现
打开界面之后,点击提交按钮会出现卡顿,最总表现的结果就是卡死,无响应...
问题排查
使用F12进行查看,当点击之后发现它会发送11个请求,其中10个上传base64的请求,1个上传报告数据的接口。10个上传base64的接口一直处于padding中,并且会在接近1分钟之后给出响应结果。既然没有报错,还是在正常处理的,难道是后端解析base64时内存不够导致的?(客户的服务器内存很小),cpu占用过高?
本着发现问题,就地复现的原则,我准备直接上客户服务器上看看,这样更容易发现问题。
登录了客户的机器,使用
htop
查看机器的CPU和memory的情况,点击按钮之后发现占用几乎没有什么波动???纳尼???什么情况啊,这....于是准备查一下日志,在这个上传base64和上传报告的接口上开始入手,看看是什么情况,结果发现上传base64的接口居然使用了1分钟左右???
嗯?回到最开始的点了,到底是啥情况?服务器的资源没啥变化啊?于是开始在这个接口不同的地方写入日志,请求后查看结果。最终锁定到一个函数上了
resolve_conflict
嗯?一个文件名称重命名的工具函数罢了,还能闹出什么事情?这么离谱?怎么回事?点开源码看一下
pythondef resolve_conflict(self, target_folder, basename): name, ext = os.path.splitext(basename) count = 0 while True: count = count + 1 newname = '%s_%d%s' % (name, count, ext) if not os.path.exists(os.path.join(target_folder, newname)): return newname
注:这是flask-uploads官方的源码源码地址,我们的业务中完全抄了这个方法,不过是写成了函数。
看起来没啥问题啊,但是为啥用while True啊,看着好瘆人啊,这不是死循环么,于是去瞟了一眼文件资源保存的地方,这一看吓一跳,文件名基本都是
image_xxx.png
的,并且还有2万张,好家伙,知道问题了我们的“前端”比较偷懒,上传的base64图片文件名称全部都叫
image
,而后端解析之后就很直接的使用了这个名称来保存文件....,结果在保存时,由于文件重名了,就会自动调用这个重命名的函数进行重命名。你想一下每上传一张图片都需要循环2万多次咧,关键客户的机器还是机械硬盘,你品,你细品....解决方案
既然知道了问题所在,解决问题就很简单了,在保存文件时给文件名加上一个时间戳,这样即使是并发执行,对于小业务的场景,咱们也不用空转2万多次啊,而且图片会越来越多的,这样不至于让它空转那么久啊。