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
php 大文件上傳 redis+php resque 較低io消耗