訂閱以接收新文章的通知:

藉助 Workers、 Durable Objects 和 Queues 讓 Super Slurper 速度提升 5 倍

2025-04-10

閱讀時間:8 分鐘
本貼文還提供以下語言版本:EnglishFrançaisDeutsch日本語한국어Español简体中文

Super Slurper 是 Cloudflare 的資料遷移工具,旨在簡化雲端物件儲存提供者與 Cloudflare R2 之間的大規模資料傳輸。自其推出以來,成千上萬的開發人員使用 Super Slurper 將 PB 級的資料從 AWS S3、Google Cloud Storage 和其他與 S3 相容的服務移至 R2。

但我們看到了一個可進一步加速效能的機會。我們使用開發人員平台(在 Cloudflare WorkersDurable ObjectsQueues 基礎上構建),從頭開始重新架構 Super Slurper,並將傳輸速度提升高達 5 倍。在這篇文章中,我們將深入探討原始架構、我們識別的效能瓶頸、我們如何解決這些瓶頸,以及這些改善對真實世界的影響。

初始架構與效能瓶頸

Super Slurper 最初與 SourcingKit 共用其架構,後者是專為將影像從 AWS S3 大量匯入 Cloudflare Images 而構建的工具。SourcingKit 部署在 Kubernetes 上,與 Images 服務一起執行。當我們開始構建 Super Slurper 時,我們將其拆分為自己的 Kubernetes 命名空間,並引入了一些新的 API,使其更方便用於物件儲存使用案例。此設定運作良好,並協助成千上萬的開發人員將資料移至 R2。

然而,它並非沒有挑戰。SourcingKit 並非設計用於處理 PB 級的大規模傳輸。SourcingKit 透過延伸 Super Slurper 在位於我們其中一個核心資料中心的 Kubernetes 叢集上運作,這意味著它必須與 Cloudflare 的控制平面、分析和其他服務共用運算資源和頻寬。隨著遷移數量的增長,這些資源限制明顯成為了瓶頸。

對於在物件儲存提供者之間傳輸資料的服務來說,工作很簡單:列出來源中的物件,將其複製到目的地,然後重複操作。這正是原始 Super Slurper 的運作方式。我們列出了來源貯體中的物件,將該清單推送至基於 Postgres 的佇列 (pg_queue),然後以穩定的步調從此佇列中提取物件,以將物件複製過來。考慮到物件儲存遷移的規模,頻寬用量不可避免地會很高。這使得擴展變得極具挑戰性。

為了解決僅在我們的核心資料中心運作的頻寬限制問題,我們在組合中引入了 Cloudflare Workers。我們並非在核心資料中心處理資料複製,而是開始呼叫 Worker 來進行實際複製:

隨著 Super Slurper 用量的增長,我們的 Kubernetes 資源消耗亦隨之增加。在資料傳輸期間,花費了相當長的時間等待網路 I/O 或儲存,而非實際執行運算密集型任務。因此,我們需要的不是更多記憶體或更多 CPU,而是更高的並行處理能力。

為了跟上需求,我們不斷增加複本計數。但最終我們碰壁了。在數十個數量級的 Pod 上執行時,我們面臨著可擴展性挑戰,而此時我們希望增加成倍的數量級。

我們決定從首要原則開始,重新思考整個方法,而不是依賴於我們繼承的架構。在大約一週的時間裡,我們使用 Cloudflare WorkersDurable ObjectsQueues 構建了一個粗略的概念驗證。我們列出了來源貯體中的物件,將其推送至佇列,然後取用佇列中的訊息以啟動傳輸。雖然這聽起來與我們在原始實作中所做的非常相似,但在我們的開發人員平台上構建,可讓我們自動擴展一個數量級,比之前高一個數量級。

  • Cloudflare Queues:支援非同步物件傳輸和自動擴展,以滿足遷移的物件數量。

  • Cloudflare Workers:在沒有 Kubernetes 開銷的情況下執行輕量型運算任務,並最佳化流程每個部分的執行位置,以降低延遲並提高效能。

  • SQLite 支援的 Durable Objects (DO):充當完全分散式資料庫,從而消除單一 PostgreSQL 執行個體的限制。

  • Hyperdrive:提供從原始 PostgreSQL 資料庫快速存取歷史工作資料的服務,並將其作為封存儲存。

我們執行了一些測試,發現我們的概念驗證對於小型傳輸(幾百個物件)來說比原始實作慢,但當傳輸擴展至數百萬個物件時,其速度則相當並最終超出原始執行效能。這就是我們需要投入時間,將概念驗証投入生產的訊號。

我們消除了概念驗證障礙,竭力提升穩定性,並尋找新的方法來使傳輸擴展至更高的並行度。經過幾次反覆運算,我們取得了滿意的結果。

新架構:Workers、Queues 和 Durable Objects

處理層:管理遷移流程

處理層的核心是佇列、取用者和工作者。流程如下:

啟動遷移

用戶端觸發遷移後,首先會向我們的 API Worker 傳送一個請求。此 Worker 或取得遷移的詳細資料,將其儲存在資料庫中,並將一則訊息新增至 List Queue 以啟動該流程。

列出來源貯體

List Queue Consumer 是事情開始好轉的階段。這會從佇列中提取訊息,從來源貯體擷取物件清單,套用任何必要的篩選條件,並將重要的中繼資料儲存在資料庫中。然後,透過將物件傳輸訊息排入佇列 Transfer Queue 來建立新任務。

我們會立即將新的批次工作排入佇列,從而最大限度地提高並行處理能力。當發生非預期失敗時(例如,從屬系統關閉),內建的節流機制可防止我們向佇列新增更多訊息。這有助於在中斷期間維持穩定性並防止過載。

高效的物件傳輸

Transfer Queue Consumer Workers 從佇列中提取物件傳輸訊息,透過鎖定資料庫中的物件金鑰,來確保每個物件僅被處理一次。傳輸完成後,物件即會解鎖。對於較大的物件,我們將其分解為可管理的區塊,並以多部分上傳進行傳輸。

從容地處理失敗

在任何分散式系統中,失敗都是不可避免的,我們必須確保將其考慮在內。我們對暫時性失敗實作了自動重試,因此問題不會中斷遷移流程。但是,如果無法透過重試解決問題,訊息將進入 無效信件佇列 (DLQ),並在此被記錄以供日後審查和解決。

工作完成與生命週期管理

在列出所有物件且傳輸在進行中後,Lifecycle Queue Consumer 會密切關注所有物件。它會監控進行中的傳輸,從而確保不會留下任何物件。完成所有傳輸後,工作會標記為已完成,遷移流程結束。

資料庫層:持久儲存與舊版資料擷取

在構建新架構時,我們知道,我們需要一個強大的解決方案來處理龐大的資料集,同時確保擷取歷史工作資料。這正是我們的 Durable Objects (DO)Hyperdrive 組合的用武之地。

Durable Objects

我們為每個帳戶提供了一個專用的 Durable Object 來追蹤遷移工作。每項工作的 DO 會儲存重要的詳細資料,例如,貯體名稱、使用者選項和工作狀態。這可確保一切井然有序且易於管理。為了支援大型遷移,我們還新增了一個 Batch DO,用於管理排入佇列的傳輸物件,儲存其傳輸狀態、物件金鑰,以及任何額外的中繼資料。

隨著遷移規模擴展至數十億個物件,我們必須在儲存方面發揮創造力。我們已實作分片策略來分散要求負載,從而防止瓶頸並解決 SQLite DO 的 10 GB 儲存限制。隨著物件的傳輸,我們會清除其詳細資料,並在此過程中最佳化儲存空間。十億個物件金鑰需要多大的儲存空間,實在令人驚訝!

Hyperdrive

由於我們正在重建的系統具有多年的遷移歷史,因此我們需要一種方法,來保留和存取過去的每一個遷移詳細資料。Hyperdrive 可充當通往舊版系統的橋樑,能夠從我們的核心 PostgreSQL 資料庫無縫擷取歷史工作資料。它不僅是一種資料擷取機制,更是適用於復雜遷移情境的封存。

結果:Super Slurper 現在將資料傳輸至 R2 的速度提升高達 5 倍

那麼,在完成所有這些操作之後,我們真的實現了加速傳輸的目標嗎?

我們執行了一項測試,將 75,000 個物件從 AWS S3 遷移至 R2。在最初的實作中,遷移需要 15 分 30 秒。在我們提高效能之後,只需 3 分 25 秒即可完成相同的遷移。

當生產遷移於 2 月開始使用新服務時,我們發現,在某些情況下還有更大的改善,尤其在取決於物件大小的分散時。Super Slurper 已經存在大約兩年了。而效能的改善使其能夠移動更多資料 — 在 Super Slurper 複製的所有物件中,有 35% 是在過去兩個月內複製的。

挑戰

使用新架構時,我們面臨的最大挑戰之一是處理重複的訊息。出現重複的情況有以下幾種:

  • 佇列提供至少一次傳遞,這意味著,取用者可能會多次收到相同的訊息以保證傳遞。

  • 失敗和重試亦可能會造成明顯的重複項。例如,如果在傳輸 Durable Object 之後對 Durable Object 的要求失敗,則重試可能會重新處理同一物件。

如果處理不當,可能會導致多次傳輸同一個物件。為了解決此問題,我們實作了幾種策略,來確保每個物件都準確計入且僅轉移一次:

  1. 由於是按順序列出(例如,若要取得物件 2,您需要列出物件 1 的連續權杖),我們會為每個列出操作指派一個序列 ID。這讓我們能夠偵測重複的清單,並防止多個流程同時啟動。這之所以特別有用,是因為我們需要等待資料庫和佇列操作完成,才會列出下一個批次。如果列出清單 2 失敗,我們可以重試;如果列出清單 3 已經開始,我們可以減少不必要的重試。

  2. 每個物件在傳輸開始時都會被鎖定,從而防止平行傳輸同一物件。成功傳輸後,透過從資料庫中刪除其金鑰來解鎖該物件。如果稍後再次出現該物件的訊息,且金鑰不再存在,則我們可以放心地假設其已被傳輸。

  3. 我們依賴於資料庫交易來保持計數的準確性。如果物件解鎖失敗,其計數保持不變。同樣,如果將物件索引新增至資料庫失敗,則不會更新計數,並且稍後將重試操作。

  4. 作為最後的故障保護,我們會檢查目標貯體中是否已存在物件,以及是否在開始遷移之後發佈。如果是,我們假設其由我們的流程(或其他流程)傳輸,並且安全地略過。

Super Slurper 的下一步行動是什麼?

我們一直在探索讓 Super Slurper 更快捷、更具可擴展性且更易用的方法,而這僅僅是開始。

  • 我們最近推出了從任何 S3 相容儲存提供者進行遷移的功能!

  • 資料遷移目前仍限於每個帳戶同時遷移 3 個項目,但我們希望提高該限制。這將允許對物件字首拆分為單獨的遷移項目,並且可平行執行,從而顯著提高遷移貯體的速度。如需有關 Super Slurper 以及如何將資料從現有物件儲存體遷移至 R2 的詳細資訊,請參閱我們的文件

附註:作為本次更新的一部分,我們將與 API 互動變得更便捷,因此,現在能夠以程式設計方式來管理遷移

我們保護整個企業網路,協助客戶有效地建置網際網路規模的應用程式,加速任何網站或網際網路應用程式抵禦 DDoS 攻擊,阻止駭客入侵,並且可以協助您實現 Zero Trust

從任何裝置造訪 1.1.1.1,即可開始使用我們的免費應用程式,讓您的網際網路更快速、更安全。

若要進一步瞭解我們協助打造更好的網際網路的使命,請從這裡開始。如果您正在尋找新的職業方向,請查看我們的職缺
Developer WeekR2 Super SlurperCloudflare WorkersDurable ObjectsCloudflare QueuesQueues

在 X 上進行關注

Cloudflare|@cloudflare

相關貼文