php 大文件上传 redis+php resque 较低io消耗

在做视频站的时候存在这么一个情景,用户需要上传较大的视频文件,一般在两个G以上,且可能存在多人同时上传的情况。

经过查阅资料解决方案有如下几种:

1、调整php和nginx文件上传的最大限制

2、在前端将文件切片上传后再进行合并

经过我们的评估发现,方案1的做法并不合适,单纯的调大文件大小限制,会导致上传一个文件需要一个连续很长的时间, 占用一个php进程,且可能出现超时等各种情况。

所以我们选择方案二进行探索:

使用百度webuploader前端插件对大文件进行切片处理,等待所有文件全部上传完毕后再进行合并。

但是用着用着凸显出一些问题:

1、大文件在完全上传完毕后,把一个一个小的切片文件合并成一个大文件,这个时间可能是很长的,很有可能超过php或者nginx的超时限制,这样给用户的就是超时提醒,是极为不友好的。

2、文件在合并的时候需要连续长时间的写入到磁盘中,对服务器io压力很大,如果mysql在同一台服务器上也将对整个网站产生压力。

3、多人同时上传,且差不多同一时间上传完毕,再合并对服务器的压力将更大。。。超时问题也将更加的严重。

4、php接收上传的文件临时区默认是在磁盘上,整个流程就是接收->写入磁盘->上传完毕->读取切片文件->写入磁盘,整个流程对服务器压力较大。

经过查找资料(但是没找到太多,难道是关键词不对)我们想出了如下大致的解决方案

1、修改php文件上传存放的临时目录,放到ubuntu分配的内存文件目录,减少io。

2、将上传完毕再合成修改为边上传边合并,这样用户发完最后一个文件就能很快的得到上传成功或失败的提醒,同时减少了上面提到的连续长时间磁盘io。

第1点没什么说的,修改php配置即可,但是需要注意的是内存的大小限制。

难点主要在第2点中:

具体如下:

1、切片上传的文件是无序的,可能上传切片的顺序是1、5、3、4、2这样的,如何保证合并的顺序?

2、同一用户同时上传多个文件?

3、不同用户上传同名文件,如何命名?

这里主要针对难点1的解决方案进行阐述:

需要的两个工具:redis和任务调度框架php resque

利用redis维护

一个有序集合(存储已经上传的切片文件编号,有序是因为合并切片需要切片的顺序)

and

一个普通列表(存储已经合并的切片编号,用于判读切片是否连续)

php resque去发起合并任务(每个上传的文件对应唯一的任务标志id)

根据php resque的任务状态确保同一个文件同时只有一个合并任务在进行

每次切片上传成功后,向该上传文件的有序集合中,添加一个数据,键为切片序号,值也为切片序号

php resque中的job内容:参数主要为(临时文件名、切片文件总长度,最终保存的文件名)

job:

循环执行:

从有序集合从取第一小的切片号,判断该切片号-1是否已经在已经合并的切片的普通列表中(切片为1直接开始合并),不在的话循环结束

读取切片号对应的文件,写入到最终的文件中,最刚开始不存在则创建。

写入完毕后,向切片列表中加入该切片编号

删除该切片文件

继续循环

如果切片号==文件切片长度,任务over

原文地址:https://www.cnblogs.com/Lynwood/p/9650603.html