大文件下载的一些经验
Tag 下载, cdn, on by view 45

数年之前,我在一家CDN公司上班。在CDN项目主要工作是负责对客户视频数据搬运到云存储,中间也涉及到下载转码上传之类的工作。其中踩过一些坑,也吸取了一些经验。下面简单讲一下我遇到的几个问题。

下载没网速了

首先是下载没网速的情况。在处理大量的下载任务过程中,发现了一些特殊情况,客户通知我的服务回源,我的下载服务从源站下载一些大文件的时候,开始网络一切正常,可是逐渐的网速越来越低,甚至没网速,这回导致下载一直卡主。可能你会说,设置一个下载超时不就好了,但是,由于客户的文件大多是影视原片,体积巨大,有些甚至达到了几十G,如果简单的设置超时,以处理时间判断下载成败,那么这些大文件基本上不可能成功,都会以超时失败。当然,也可以分片下载来解决文件大的问题,但是对于0网速或者只有几B/s的网速,你的分片就显得无力了。其实,这个状况产生的原因大多是因为源站故障导致的,比如负载高之类的。只要断开连接重新下载就有机会恢复正常速度的连接,而且一般源站是有多个的,重新连接到新的节点就好了。所以,这里“断开连接重新发起下载”这个能力就非常重要了。我原本使用的是github.com/nareix/curl这个库进行文件下载,它的优点是创建异步下载任务后,可以监控当前下载的速度。这样,我可以对每个下载任务的网速进行监控,当网速低于一个阈值的时候,就放弃下载并重试。最初的时候只是简单的调用它的Close()方法,关闭了下载。后面却发现,经常发生fd用完的情况,一查才知道Close()方法并不能释放连接。最后只好自己修改了这个库,给它添加了强制关闭连接的能力。于是就完美的解决了这一问题。记得当时有一次,源站故障了,客户反馈说,它们接入的多家CDN厂商都上报,文件下载失败了,只有我们公司文件下载成功了,听到这消息,我嘴角不由得上翘,漏出了得意的笑容。

并行写同文件

遇到另一个印象深刻的问题就是某客户反馈文件错误的问题。客户反馈说他们在我们CDN上的文件播到一半卡死了,查了文件之后,发现文件大小正常,但是文件只有前半截有数据,后半截全是0,空的。当时,老板CTO领导全站在我工位后面,讨论这个问题,我也是第一时间加回了MD5校验,因为之前在我同事的要求下(同事是老员工、他负责任务调度、我负责几个agent,相当于他为主),去掉了上传前MD5校验,以节省任务时间。当时非常紧张,但是一时间也查不出原因,只能先加MD5校验,然后开发专用校验程序,将半年前开始的所有上传文件重新拉下来做MD5校验,查出异常文件重新上传。后来在一个偶然的机会下,发现了故障的原因,居然是调度测下发任务之后,等待N分钟之后agent还没上报成功,于是又重新下发了这个任务,然后刚好是同一个节点的agent收到该任务,这样一来,同一个节点上2个相同的任务在处理,文件路径一致,也就是说,有2个线程在从源站拉文件写到本地磁盘的同一个路径,一先一后,先处理完的线程开始上传了,后处理的还在写文件,后处理的写文件之后就会将该文件后半截变为空(这里与文件打开模式有关),这样前一个线程在上传的时候,文件其实变成半截了,再加上没有MD5校验,所以,故障就此发生了。

所以之后我就给每个任务的本地路径前加了一个uuid目录,这样一来,不管任务是否同一个文件路径,都不会相互干扰。每个任务都有自己的独立路径了。原程序是我接手别人的,虽说这问题不是我引入,但是,平心而论换做当时的我,也是想不到这一点的。不过,吃一堑长一智,现在只要涉及到本地资源存储,我基本上都会给路径前添加uuid前缀。