背景介紹
OSS 簡單的接口和卓越的可擴充性讓不同場景的應用程式每天可以輕松存儲幾個到幾十億個對象檔案。簡單的 key/value 資料通路結構極大地簡化了資料的上傳和讀取。然而,除了上傳和讀取,很快就圍繞 OSS 産生了一系列新的應用場景,舉幾個例子:
- 海量 OSS 檔案複制 (bucket 内或跨 bucket),改變存儲類型(标準 -> 歸檔)節省成本
- 大并發 OSS 檔案解凍(restore)将備份的歸檔檔案恢複後供應用使用
- 事件觸發超大檔案解壓,GB 級别壓縮包,包含十萬級别以上的檔案需要在上傳後被自動解壓到一個新的 OSS 路徑下
上面 3 類場景有一些有共性的挑戰:
- 總處理時間長: 處理億級别的 OSS 檔案數即使是高并發通路 OSS, 總耗時也是天級别甚至更長
- 大量遠端調用可能産生的異常處理:由于 OSS API 基本都是操作單個檔案,處理幾百萬到幾千萬個檔案就意味着等數量級的遠端調用。在分布式系統中,這些遠端調用失敗都需要處理。
- 狀态持久化:需要有類似 checkpoint 的機制減少在部分失敗的情況下全部重新處理,避免浪費時間 (如批量處理可以跳過已經處理過的前 1000 萬個 key)。
在這篇文章中我們将就上面提到的 3 個場景介紹一個基于 Serverless 工作流和函數計算(FC)無伺服器最佳實踐解決方案。
海量 OSS 檔案複制 + 歸檔
OSS 檔案備份聽起來是一個簡單的 list-and-copy 的主程式就可以搞定的問題,現實中有非常多的問題需要考慮:例如主程式運作中如果機器當機或者程序異常退出後,如何自動恢複(自己實作高可用)?恢複後如何快速已經被處理過的檔案 (自己寫資料庫維護狀态)?如何協調主備程序 (自己實作服務發現)?如何縮短複制時間? (自己實作并行調用和管理) 基礎設施的維護成本和經濟成本和可靠性如何取舍?在幾億個 OSS 對象面前,一個簡單的單線程 list-and-copy 的主程式已經無法可靠地滿足這類需求。
假設使用者某 bucket 下有幾億 OSS 檔案需要被複制到同一 region 不同 bucket 下,且需要将标準存儲類型轉換成歸檔存儲。 在這個
oss-batch-copy 示例中,我們提供了一個工作流應用模闆将使用者提供的索引檔案中的所有檔案依次調用函數計算 OSS CopyObject 操作實作備份。索引檔案中包含需要被處理的 OSS object meta,示例如下:

幾億個 OSS 檔案的索引也會在百 GB 級别,是以需要利用 range 讀分頁讀取索引檔案,每次處理一部分 OSS 檔案并且需要一個類似
while hasMore {}
的控制邏輯保證整個索引檔案從頭到尾被處理。使用 Serverless 工作流的實作邏輯如下:
-
任務步驟:從輸入的索引檔案位置 (offset) 讀取一段輸入提供的長度 (size)從中提取需要被處理的檔案并調用 FC 函數調用 OSS CopyObjectcopy_files
-
選擇步驟:成功處理完一批檔案後,通過條件比對判斷目前索引檔案是否已經被全部處理,是則進入成功步驟,否則将下一頁(offset, size)傳入 copy_files 循環執行。has_more_files
-
任務步驟:由于單個工作流執行有曆史事件(history events)數限制,在該選擇步驟也會根據目前工作流的事件 ID 判斷,如果目前事件數已經超過一個門檻值,則觸發一個新的相同的流程執行,該流程會在子流程結束後繼續進行。子流程也可能觸發它的子流程,這樣層層遞歸保證了無論多少 OSS 檔案,整個流程都可以處理完成。start_sub_flow_execution
使用工作流實作批量處理提供了如下保證:
- 單次處理的時間幾乎可以任意長度,任意多的檔案數:工作流支援最長 1 年的執行
- 免運維,無需自行實作高可用:工作流和函數計算都是高可用的 Serverless 雲服務
- 無需自己實作 checkpoint, 狀态維護等邏輯:如果因為任何原因流程失敗,可以重新從最近成功的一個 offset 開始執行。這過程中不需要使用任何的資料庫或者隊列。
- 失敗重試配置:通過指數退避的配置可以處理大多數的瞬時遠端調用錯誤。
高并發批量解凍 OSS 檔案
文章
基于 Serverless 工作流高并發批量解凍 OSS 檔案介紹了一種高效可靠解凍大量 OSS 歸檔檔案的解決方案。該場景和複制檔案有類似的挑戰,但也有其特殊性:
- 和 CopyObject 不同,Restore 操作是異步的,即觸發後需要輪詢該對象狀态才能能確定解凍完成
- 單個對象解凍時間在分鐘級,可能随着對象大小變化。這要求整個流程有更高的并發保證解凍在規定的時間内完成。
和 oss-batch-copy 類似的邏輯,該
示例通過 ListObjects 分批 restore OSS,其中每一批解凍都是一個
子流程。在每個流程中使用
foreach 并行循環步驟高并發解凍 OSS 對象(最高 100 并發)。由于 Restore 接口是異步操作,是以在每次 Restore 過後需要輪詢該 object 的狀态直到解凍完成。解凍和輪詢在同一個并發分支中完成。
使用工作流+函數計算批量解凍的特點:
- 天然支援高并發解凍,縮短整體耗時
- 有狀态可靠的輪詢確定流程結束時所有對象都解凍完成
事件驅動解壓超大 OSS 檔案
OSS 的一大作用是做檔案的共享存儲,如一方上傳處理好的内容供下遊應用使用。由于多個檔案上傳需要調用多次 PutObject 接口出錯機率較大也不容易實作,很多上遊服務使用壓縮包用一個接口調用就完成上傳。這樣雖然簡化了上傳方,然而下遊的使用方希望看到的是維持原本結構的檔案以便消費。這裡的需求就是響應 OSS 檔案上傳事件,自動将一個壓縮包解壓存放到另一個 OSS 路徑。今天控制台上已經有一個通過事件觸發函數計算執行解壓的功能,然而目前單純基于函數計算的方案有一些問題:
- 單個函數 10 分鐘的執行時間限制:對于 GB 級别的壓縮包,或者壓縮包内有海量小檔案的場景很容易執行逾時導緻解壓失敗
- 容錯性低:OSS 異步調用函數計算,函數内通路 OSS 存在瞬時失敗的可能,FC 異步調用在函數調用失敗時會最多重試 3 次,超過次數後會丢棄消息導緻解壓失敗。
- 靈活性不夠:多個使用者提出需要在解壓後向消息服務發通知,發短信,以及删除原壓縮包等自定義需求,基于單函數不易實作。
為了解決長時間執行和自定義重試的問題,我們在這個
示例應用中引入 Serverless 工作流編排函數計算。OSS 事件觸發函數計算後啟動工作流執行。工作流會通過 ZIP 包的中繼資料流式讀取、解壓、上傳到 OSS 目标路徑。每個函數的執行時間超過一定門檻值後即傳回目前的 marker,工作流會判斷目前 marker 是否表示所有檔案處理完成,如果是則結束流程執行,否則繼續從目前 marker 繼續流式解壓,直到結束。
工作流的加入突破了函數調用10分鐘的限制,并且自帶的狀态管理和自定義重試,即使是 GB 級别大小,10萬級别檔案數的壓縮包也可以可靠地解壓。工作流最長支援一年的執行,幾乎任意大小的 ZIP 包都同樣可以流式解壓成功。
工作流為解壓流程提供了靈活的定制化能力,下圖是某使用者的在解壓結束後通知其 MNS 隊列,通知結束後進入到下一步驟删除原壓縮包。
Takeaways
可以看到 OSS 的大規模普及也帶來了一系列問題,然而解決問題的方式又繁瑣,無趣,易出錯。本文我們就批量檔案備份,大并發解凍和事件驅動解壓超大 ZIP 檔案 3 個常見場景介紹了基于 Serverless 工作流和函數計算的簡單、輕量,Serverless 解決方案高效可靠地解決以下問題:
- 支援長時間運作的流程,最長執行一年不間斷
- 狀态維護,不受系統 failover 影響
- 提高瞬時錯誤忍度度
- 高度靈活自定義
海量 OSS 檔案批量處理的場景遠不止文中提到的 3 個,我們期待更多的場景和需求的讨論也同樣歡迎對 Serverless 生态,工作流,函數計算有興趣的同學加入内部釘釘群。