断点续传遇到的问题&排查过程
前言
工作了一段时间,遇到了不少问题,也解决了不少问题,不过还是发现有必要把这些问题或者一些学习过程记录下来,一方面也是一笔财富,一方面或许能帮到一些人少走一些弯路。第一次写,还不太习惯,问题也不是很复杂,就当熟悉一下写博客的过程吧。
背景
早期在工作过程中,写了一个php脚本,fork 出多个子进程(最多10个),每个子进程都通过断点续传的方式读取远程文件(A地址),解析后写入redis(通过自己实现的hash算法,直连redis)。
最近因数据迁移等原因,升级了脚本,读取新的远程文件(B地址),写入新的redis系统(简称F-redis),F-redis实现了代理、hash等一整套功能,对客户端更加透明,经评估和压测,性能也会有所下降(最坏情况下降50%左右)。
问题&分析排查
升级脚本后,经多次观察,发现总的运行时间比之前长了很多,尤其是文件较大时,初步分析,问题主要定位在两点:
1,F-redis,读写性能较差
2,B地址远程文件,访问时耗时较长
首先主要怀疑F-redis,因此和运维进行了沟通,经过排查,发现有很多报错日志,原因是F-redis选择的是持久化方式,而持久化方式是不支持设置过期时间,需要选择缓存方式才行(取决于F-redis架构的实现,这里不过多追究),当时一个简单的想法是,或许是产生了大量的报错日志,io达到了一些瓶颈导致系统进程产生了异常,由于当时业务需要,着急把功能上线,因此一方面切换回原生redis方式,另一方面也接着排查。切换回原生redis后,经测试,发现现象依然如此,同时在运维测的配合下,也未发现脚本服务器磁盘或者进程异常,redis服务器表现也很正常,因此排除了redis的写入问题。那么难道是B地址访问耗时太长?经实际测试发现,和原有的A地址相比,的确慢了一些,但是差异也不是很大。
排除了以上两点,此时只能再回到脚本本身,代码哪里有问题呢?原有逻辑运行了一年多也没发现有异常。难道是fork的子进程有些问题?因此把每个子进程运行的结束时间打印出来进行分析,经过多次测试发现,每次运行脚本(开启了N个子进程),总是有一个子进程运行时间特别长,这就很奇怪了,按道理应该是N-1个进程结束时间差不多,最后一个进程结束时间还要早一些才对,那么这个进程发生了什么?难道是卡住了?然后根据进程号进行了跟踪,但是发现执行过程很顺利,也没发现任何异常,这个时候感觉思路有些卡住了,有同事提建议说,php fork子进程的方式并不太好,可能会发生未知的问题,最好改成通过crontab启动多个进程的方式,想了想的确是有这个可能。
心动不如行动,动手改造!码码码之后,新鲜的程序出炉了!经过局部测试之后,也比较简单,没发现什么问题,那就正式跑一下整体功能吧!然后呢。。现象依旧。。。问题在哪里呢?不能轻言放弃,于是决定把每个进程写redis的key等信息都记录到文件中看看,总共4个进程,前三个进程跑完之后,第四个依然在跑,看记录的信息很正常,没有卡住或者写失败之类的。看到具体的文件信息后,感觉问题已经有些明朗了,最后一个进程读的文件长度应该最短,应该最先完成才对,怎么反而是读取的内容最多的呢,那么肯定是最后一次读远程文件内容时有问题!也就是说断点续传最后一次是有问题的!
经过简单的测试,发现最后一次通过断点续传获取内容时,当指定的结束字节数比文件总字节数大的话,B地址直接返回了全部文件内容,这个真是没想到,讲道理的话,支持断点续传的服务端,发现结束字节数比文件总字节数大的话,应该是默认取到文件末尾才对,但是不知道B地址服务端如何实现的,居然返回了全部文件内容,因此导致最后一个进程,其实是读取了整个文件内容,所以执行时间最长。。既然知道具体问题所在,那就很好解决了,这里不在赘述。
总结
1,有时候遇到问题,可能是你完全没想到的地方出了问题,排查过场中可能发生很多误导,也会走不少弯路,排除所有不可能,剩下的就是唯一的可能。
2,遇到瓶颈了,可以尝试和其他人去沟通一下,或许会有所提醒,当然最重要的还是得靠自己日积月累的知识和经验了。
3,整个过程中,其实是有过想放弃的念头,但是都立马被自己否决了,这个一定要坚持一下,答案可能就在下一次尝试后。
浙公网安备 33010602011771号