管理資源調度風險

Cloud Tasks 可讓您調度大量非同步工作,同時控管流量到達佇列和下游服務的速率。套用逐步增加模式和速率控管機制,即可順利調度工作負載資源,避免流量增加速度超過基礎架構適應速度時,觸發佇列延遲時間高峰或目標超載。

總覽

本文提供最佳做法指南,說明如何在高流量佇列中維持 Cloud Tasks 的高效能。高 TPS 佇列是指每秒建立或調度 500 個以上任務 (TPS) 的佇列。「高 TPS 佇列群組」是一組連續的佇列,例如 [queue0001、queue0002、…、queue0099],每秒建立或調度的任務總數至少為 2,000 個。您可以使用 Cloud Monitoring 指標,查看佇列或佇列群組的歷來 TPS,其中 api/request_count 代表 CreateTask 作業,queue/task_attempt_count 代表工作嘗試。高流量佇列和佇列群組容易發生兩大類故障:

如果建立工作並調度至個別佇列或佇列群組的速度,快於佇列基礎架構的適應速度,就會發生佇列超載。Google 的基礎架構採用調整式層級,會根據流量模式動態重新分配負載,而這項調整需要時間。同樣地,如果工作調度率導致下游目標基礎架構的流量暴增,就會發生目標超載。無論是哪種情況,都請遵循 500/50/5 模式:當擴充超過 500 TPS 時,每 5 分鐘增加的流量不超過 50%。

與高 TPS 工作負載的 500/50/5 模式不同,初始佇列漸進式增加會影響所有從冷啟動或閒置狀態開始的佇列,包括設定為中等調度率的佇列。本文將探討可能導致資源調度風險的不同情境,並舉例說明如何管理佇列擴充,以及套用 500/50/5 模式。

初始佇列增加

500/50/5 模式適用於已穩定運作的高 TPS 佇列。初始佇列漸進式增加是一項獨立的系統機制,適用於所有佇列,包括設定為中等調度速率 (例如 20-100 TPS) 的佇列,因為這些佇列會從冷或閒置狀態轉換為穩定狀態作業。

在下列任一情況下,佇列會進入冷卻或閒置狀態:

  • 佇列是新建立的,或是在暫停後恢復。

  • 佇列閒置幾分鐘,沒有任何進行中的調度作業。

  • 佇列在從下游 HTTP 429 或 5xx 錯誤回應退避後,正在復原。如要進一步瞭解錯誤輪詢對調度率的影響,請參閱「輪詢錯誤和強制執行率」。

當工作抵達冷佇列或閒置佇列時,Cloud Tasks 不會立即以設定的 max_dispatches_per_second 速率調度工作。為保護下游目標免受流量突然激增的影響,Cloud Tasks 會以較低的基準速率 (例如約 1 TPS) 開始調度。然後在定期評估間隔內,逐步提高強制調度率。只要佇列需求仍處於飽和狀態,且目標處理常式傳回 HTTP 2xx 回應,強制執行的速率就會持續提高。視目標速率而定,max_dispatches_per_second 值可能需要幾分鐘才能達到高值。

如果工作負載傳送的流量會突然暴增,且每次暴增之間有幾分鐘以上的短暫閒置期,則佇列會在每次暴增之間重設為冷狀態,並在每次暴增時重複這個逐步增加的過程。如要盡可能減少突發敏感工作負載的調升延遲,請遵循下列最佳做法:

  • 維持穩定的基準流量,讓對延遲敏感的佇列在流量激增期間保持活躍。

  • 重複使用現有佇列,而非頻繁刪除及重新建立佇列。

  • 佈建足夠的下游目標容量,讓處理常式傳回 HTTP 2xx 回應,並避免傳回 HTTP 429 或 5xx 錯誤回應,重設強制執行的調度率。

診斷佇列節流

如果佇列調度任務的速度慢於設定的 max_dispatches_per_second 速率,請檢查瓶頸是否來自冷啟動或閒置佇列加速、目標擁塞退避或並行限制。

冷佇列或閒置佇列增加

如果符合下列所有條件,則可能是初始佇列增加所致:

  • 佇列是最近建立、暫停後恢復,或在流量到達前閒置了幾分鐘。
  • 工作嘗試成功,未發生目標錯誤,Monitoring 指標 cloudtasks.googleapis.com/queue/task_attempt_count 中顯示 response_code="OK"。
  • 傳送中的調度作業仍低於 max_concurrent_dispatches,而 cloudtasks.googleapis.com/queue/depth 或 cloudtasks.googleapis.com/queue/task_attempt_delays 則會提高。同時,cloudtasks.googleapis.com/queue/task_attempt_count 的調度率會在定期評估間隔內逐步提高。

如要進一步瞭解 Monitoring 指標,請參閱「將指標傳送至 Monitoring」。

目標過載和壅塞輪詢

如果下游目標傳回 HTTP 429 Too Many Requests 或 HTTP 5xx 錯誤回應 (例如 HTTP 503 Service Unavailable),Cloud Tasks 會動態降低調度率。這種壅塞輪詢延遲機制可避免目標超載。

如要確認目標錯誤是否導致 Cloud Tasks 限制調度,請查看依 response_code 標籤分組的 Monitoring 指標 cloudtasks.googleapis.com/queue/task_attempt_count。如果已啟用佇列記錄功能,您也可以在 Logs Explorer 中檢查嘗試回應記錄:

resource.type="cloud_tasks_queue"
jsonPayload.attemptResponseLog.status!="OK"

在 response_code 和 jsonPayload.attemptResponseLog.status 中,Cloud Tasks 會記錄標準 gRPC 狀態碼,而非數值 HTTP 狀態碼。舉例來說,HTTP 429 對應至 RESOURCE_EXHAUSTED、HTTP 503 對應至 UNAVAILABLE、HTTP 500 對應至 INTERNAL,而 HTTP 504 或逾時則對應至 DEADLINE_EXCEEDED。

如果發現目標傳回 RESOURCE_EXHAUSTED、UNAVAILABLE 或其他伺服器錯誤狀態,請先擴充目標服務執行個體或提高目標並行數量,再提高佇列調度限制。

如要進一步瞭解如何啟用佇列記錄,請參閱「搭配使用 Cloud Logging 與佇列」。

並行瓶頸

根據利特爾法則,下游目標延遲時間會限制最大調度處理量:

Max Throughput (tasks/sec) = min(
  max_dispatches_per_second,
  max_concurrent_dispatches / Downstream Latency in seconds
)

舉例來說,如果下游執行延遲時間為 2 秒,且 max_concurrent_dispatches 設為 100,則每秒處理量上限為 50 項工作,即使 max_dispatches_per_second 設為 500 也是如此。

如要診斷並行是否為瓶頸,請將 max_concurrent_dispatches 除以 Logging 中 jsonPayload.attemptResponseLog.attemptDuration 欄位記錄的目標回應延遲時間。將這個計算出的門檻與 Monitoring 指標 cloudtasks.googleapis.com/queue/task_attempt_count 中的實際調度率進行比較。如果實際調度量在這個門檻附近停滯不前,而 cloudtasks.googleapis.com/queue/depth 或 cloudtasks.googleapis.com/queue/task_attempt_delays 卻持續增加,請採取下列任一行動:

  • 如果下游目標有閒置容量,例如工作大部分時間都在等待外部 I/O,請增加 max_concurrent_dispatches。
  • 如果下游目標的資源使用率已經很高,提高 max_concurrent_dispatches 可能會加劇目標超載。請改為向外擴充目標執行個體,或縮短處理常式執行延遲時間。

佇列超載

流量突然增加時,佇列或佇列群組隨時可能超載。因此,這些佇列可能會發生以下情況:

  • 工作建立作業延遲時間增加
  • 工作建立作業錯誤率提高
  • 分派速度降低

為防範這種情況,請在佇列或佇列群組的建立或調度率可能突然飆升的情況下,建立控制項。一開始,每秒最多可對冷佇列或佇列群組執行 500 次作業,然後每 5 分鐘將流量增加 50%。理論上,使用這個逐步調升排程,90 分鐘後每秒作業數可增加至 74 萬。這種情況可能在多種情況下發生。

例如:

  • 推出需要大量使用 Cloud Tasks 的新功能
  • 在佇列間移動流量
  • 在佇列增減後重新平衡流量
  • 執行批次工作來置入大量工作

在這些情況或其他情況下,請按照 500/50/5 的模式操作。

使用 App Engine 流量拆分功能

如果工作是由 App Engine 應用程式建立,您可以使用 App Engine 流量拆分功能 (App Engine 標準環境或 App Engine 彈性環境),順利增加流量。透過在版本之間拆分流量 (標準環境或彈性環境),您可以隨著時間增加速率管理的要求,以保護佇列健康狀態。舉例來說,假設要增加新擴充佇列群組的流量:[queue0000, queue0199] 是一系列高 TPS 佇列,在尖峰時段總共接收 100,000 個 TPS 建立作業。

[queue0200, queue0399] 是新佇列序列。轉移所有流量後,序列中的佇列數量會加倍,新的佇列範圍會接收序列總流量的 50%。

部署增加佇列數量的版本時,請使用流量拆分功能,逐步將流量轉移至新版本,進而轉移至新佇列:

  • 首先轉移 1% 的流量到新版。舉例來說,如果每秒交易數為 100,000,則 1% 的 50% 會產生 500 TPS 至一組新佇列。
  • 每 5 分鐘將傳送至新版本的流量增加 50%,詳情請參閱下表:
自部署開始以來的分鐘數 佔轉移到新版總流量的百分比 佔新佇列總流量的百分比 佔舊佇列總流量的百分比
0 1.0 0.5 99.5
5 1.5 0.75 99.25
10 2.3 1.15 98.85
15 3.4 1.7 98.3
20 5.1 2.55 97.45
25 7.6 3.8 96.2
30 11.4 5.7 94.3
35 17.1 8.55 91.45
40 25.6 12.8 87.2
45 38.4 19.2 80.8
50 57.7 28.85 71.15
55 86.5 43.25 56.75
60 100 50 50

版本導向的流量遽增情形

推出新版本時,如果佇列或佇列群組的流量大幅增加,漸進式推出也是一項重要機制,可讓流量平穩增加。逐步推出執行個體,讓初始啟動作業不會超過每秒 500 次作業,每 5 分鐘增加不超過 50%。

新的高 TPS 佇列或佇列群組

新建立的佇列特別容易遭受安全攻擊。在初始推出階段,佇列群組 (例如 [queue0000、queue0001、…、queue0199]) 與單一佇列一樣敏感。對於這些佇列,漸進式推出是重要的策略。推出新服務或更新服務,分階段建立高 TPS 佇列或佇列群組。初始負載應低於 500 TPS,且每次增加的負載量應不超過 50%,間隔至少 5 分鐘。

新擴展的佇列群組

增加佇列群組的總容量時 (例如將 [queue0000-queue0199 擴充至 queue0000-queue0399]),請遵循 500/50/5 模式。在推出程序中,新佇列群組的行為與個別佇列相同。將 500/50/5 模式套用至整個新群組,而非僅套用至群組內的個別佇列。

對於這些佇列群組擴充,漸進式推出是重要的策略。如果流量來源是 App Engine,您可以使用流量拆分。遷移服務以將任務新增至更多佇列時,請逐步推出執行個體,確保初始啟動時對新佇列執行的作業總數不超過 500 次,且每 5 分鐘增加的流量不超過 50%。

緊急佇列群組擴展

有時您可能需要擴充現有的佇列群組,例如預期工作新增至佇列群組的速度,會快於群組調度工作的速度。如果您按照字典順序,將新佇列的名稱平均分配到現有佇列名稱中,就可以立即將流量傳送至這些佇列。請確保新的交錯佇列不超過 50%,且每個佇列的流量低於 500 TPS。這個方法是流量拆分和漸進式推出的替代方案。

如要達到這種交錯命名,請在結尾為偶數的佇列附加後置字串。舉例來說,如果您有 200 個現有佇列 [queue0000-queue0199],並想建立 100 個新佇列,請選擇 [queue0000a、queue0002a、queue0004a、…、queue0198a] 做為新佇列名稱,而非 [queue0200-queue0299]。

如果需要進一步提高,您仍可每 5 分鐘交錯增加最多 50% 的佇列

大規模或批次工作排入佇列

如需新增大量工作 (例如數百萬或數十億),雙重插入模式會很有幫助。請改用注入器佇列,不要從單一工作建立任務。新增至注入器佇列的每項工作都會展開,並將 100 項工作新增至相關佇列或佇列群組。您可以逐步加快插入器佇列的速度,例如先以 5 TPS 在目標佇列或佇列群組中產生 500 TPS,然後每 5 分鐘將速率提高 50%。

已命名的工作

建立新任務時,Cloud Tasks 預設會為任務指派專屬名稱。您可以使用 name 參數為工作指派自己的名稱。不過這會對效能造成大量負擔,加重延遲情形,且可能會提高命名工作相關的錯誤率。依序命名的工作名稱 (例如時間戳記) 可能會大幅增加這項負擔。

如果您自行指派名稱,請為工作名稱使用分布良好的前置字串,例如內容的雜湊。如要進一步瞭解如何命名工作,請參閱 Task.name 欄位參考資料。

目標超載

如果佇列調度作業突然激增,Cloud Tasks 可能會使您使用的下游服務和資源超載,例如 App Engine、Firestore (Datastore 模式) 和網路。如果累積了待處理工作,取消暫停這些佇列可能會導致這些服務超載。為避免目標超載,請遵循 500/50/5 模式:如果佇列調度超過 500 TPS,則每 5 分鐘增加的佇列觸發流量不得超過 50%。如要主動監控流量增加情形,請使用監控指標。您可以使用 Monitoring 快訊偵測潛在危險情況。

取消暫停或繼續高 TPS 佇列

恢復暫停的佇列時,Cloud Tasks 會套用初始佇列加速,並從低基準逐漸提高調度率,直到達到佇列設定的 max_dispatches_per_second 速率。不過,如果高 TPS 佇列或同時恢復的多個佇列中累積大量待處理工作,調度率仍可能比下游目標服務擴充的速度更快。如要避免在恢復有大量待處理工作的佇列時,目標超載,請錯開恢復多個佇列的時間。或者,您也可以暫時降低 max_dispatches_per_second 速率,然後按照 500/50/5 模式逐步提高。如要進一步瞭解 Cloud Tasks 如何在佇列恢復時限制及提高調度作業,請參閱「輪詢錯誤和強制執行速率」。

大量排定的工作

如果排定同時調度大量任務,也可能導致目標超載。如需一次啟動大量工作,建議使用佇列速率控制項,逐步提高調度速率,或預先佈建目標容量。

增加擴散傳遞

更新 Cloud Tasks 呼叫的服務時,增加遠端呼叫數量可能會造成正式環境風險。舉例來說,假設高 TPS 佇列中的工作呼叫處理常式 /task-foo。舉例來說,如果新版本在處理常式中新增多個昂貴的 Datastore 呼叫,則新版本可能會大幅增加呼叫 /task-foo 的成本。這類發布作業的最終結果是 Datastore 流量大幅增加,且與使用者流量的變化直接相關。使用漸進式推出或流量拆分功能,管理逐步提高流量的過程。

重試次數

進行 Cloud Tasks API 呼叫時,程式碼會在發生失敗時重試。不過,如果伺服器端錯誤導致大量要求失敗,高重試率可能會進一步造成佇列超載,並拖慢復原速度。如果用戶端偵測到有相當比例的要求因伺服器端錯誤而失敗,請限制傳出流量。舉例來說,您可以使用「網站可靠性工程」一書中「處理超載」一章所述的自動調整節流演算法。Google 的 gRPC 用戶端程式庫會實作這個演算法的變化版本。