其他
导读一个问题往往是由多个小的不规范或错误累积而成的。本文记录了作者发现问题、现象分析、排查过程、最后解决问题的全历程。项目背景我所在的项目组主要负责对店铺招牌拍摄,我负责App客户端的开发工作。此项目从立项之初到现在已经有很长的历史了。现在出现了一个问题:用户在拍摄照片时,会出现照片损坏的情况,这个问题在线上环境出现了有一段时间了,再加上自己接手时,此问题已经出现了,就没有深入排查过产生原因。暂时的解决策略是让用户手动删除损坏的照片,上传图片时,服务端也会进行一次文件损坏检测。我们会下发各种拍摄任务类型,有的任务只需要拍摄几张照片即可,有的任务需要拍摄上千张图片,此问题就会更容易暴露。在同事的建议下,决定要找到问题的根源。现象之前只是知道有此问题,没有仔细研究过。经过自测+了解,初步明确了以下现象:现象1:不同任务类型都有此问题目前项目内的不同任务类型都共用同一个拍照存储模块。此现象可以明确,出错范围是在底层拍照存储模块,而不是在上层的业务逻辑。现象2:1/200的概率稳定出现图片损坏通过与同事的共同复现,发现连续拍摄200多张的时候,就会出现一张损坏的图片。这中间我们复现好多次,出现频率都很符合预期,甚至有一丝诡异,因为这个bug出现频率太稳定了,反而有些不正常了。面对此现象,当时想到了2种可能的情况:概率和1/256(16进制的FF转为十进制的值,2的8次方,一字节[Byte]的大小)很接近,是不是由于在解析到某一字节时,出现了异常。每拍摄200多张,此时就出现重GC+手机温度过高导致降频,导致了卡顿,造成某一步执行超时或者失败。以上只是猜测,完全没有任何证据,只是当时的思考方向。现象3:仅webp格式会出现此问题目前拍摄的图片有两种存储格式,分别是jpeg和webp格式。项目之前都是使用JPEG作为存储格式,后来为了减小图片的大小,开始改用webp格式进行存储。当我们把存储格式改为jpeg时,此问题不会出现;换为webp格式时,就是出现此问题。统计了这两者的整体耗时(从图片字节流到存储到文件中),webp的用时大概是jpeg耗时的5倍;jpeg的存储大小是webp大小的1.5倍左右。面对此现象,当时的想法是处理图片耗时久,因而导致锁(线程锁、IO锁)竞争激烈,某一瞬间发生了数据冲突。排查过程首先熟悉了一下项目代码,下面是整个存储过程的流程图:整个流程还是比较简单易懂的,按照我当时的怀疑方向,制定了以下排查顺序:摄像头生成webp图片时出错了。代码调用逻辑出错。加密算法本身就有问题。排查方向1:压制照片时出错摄像头输出的图片在压制为webp照片的时候,就出现损坏了,而jpeg压制时不会损坏。该问题排查比较简单,只需要把未加密的原始webp图片也存储下来,与加密后无法解密的图片进行对照即可。实践之后,发现损坏的加密图片,对应的原始webp照片都是可以正常展示的。因此可以明确排除手机摄像头和压制webp图片的问题。排查方向2:加密流程产生问题调用AES加密算法的时候,调用可能会出错。比如:由于偶然情况,同一个图片被连续调用了两次加密算法。要排查此问题,需要深入阅读此部分的代码,并进行梳理。先查阅了AES加密算法的相关资料。AES是高级加密标准,在密码学中又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。这个标准用来替代原先的DES,目前已经被全世界广泛使用,同时AES已经成为对称密钥加密中最流行的算法之一。AES支持三种长度的密钥:128位,192位,256位。自己总结了一下:AES算法属于对称加密,加密和解密只需要一个相同的密钥;AES算法在对明文加密的时候,并不是把整个明文一股脑加密成一整段密文,而是把明文拆分成一个个独立的明文块,每一个明文块长度128bit;在没有填充的情况下,密文和原文长度相等。先重点看了一下线程安全问题,排查一圈,认真看了在此过程中所有涉及的共享变量,没有发现任何问题。下面梳理了加密解密流程,发现了一个很严重的问题。此问题发生在预览图片部分,代码如下:public