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回應,並避免傳回 HTTP429或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 用戶端程式庫會實作這個演算法的變化版本。