本頁說明如何使用 Pub/Sub 的「僅一次」功能接收及確認訊息,以便追蹤及防止重複處理訊息。啟用這項功能後,Pub/Sub 會提供下列語意:
訂閱者可以判斷訊息確認是否成功。
訊息順利確認後,系統不會重新傳送訊息。
郵件待處理時,系統不會重新傳送。在確認期限到期或訊息獲得確認之前,訊息都會視為待處理。
如果有多個有效傳送作業,但因確認期限到期或用戶端啟動否定確認,只有最新的確認 ID 可用於確認訊息。如果要求包含先前的確認 ID,就會失敗。
啟用「僅處理一次」功能後,訂閱者可以按照下列指南,確保訊息只會處理一次:
在確認期限內確認訊息。
維護處理訊息進度的相關資訊,直到訊息成功確認為止。
如果確認失敗,請使用處理訊息進度的相關資訊,避免重複作業。
只有提取訂閱項目類型支援「只傳送一次」功能,包括使用 StreamingPull API 的訂閱端。推送和匯出訂閱項目不支援「僅傳送一次」選項。
Pub/Sub 支援在雲端區域內僅傳送一次訊息,並根據 Pub/Sub 定義的專屬訊息 ID 執行這項作業。
建議的用戶端程式庫版本
- 為獲得最佳效能,請使用最新版本的用戶端程式庫,包括 Python 2.13.6 以上版本、Java 1.139.0 以上版本、PHP 1.39.0 以上版本、C# 3.2.0 以上版本、C++ v2.1.0、Go 1.25.1 以上版本、Node 3.2.0 以上版本,以及 Ruby 2.12.1 以上版本。
重新提交與重複
請務必瞭解預期和非預期重新遞送的差異。
如果用戶端對訊息發出負面確認,或未在確認期限到期前延長訊息的確認期限,系統就會重新傳送訊息。重新遞送視為有效,系統運作正常。
如要排解重新傳送問題,請參閱「處理重複項目」。
如果訊息在成功確認後或確認期限到期前重新傳送,即為重複訊息。
重新傳送的郵件在重新傳送嘗試期間會保留相同的郵件 ID。
啟用「僅傳送一次」的訂閱項目不會收到重複傳送的訊息。
用戶端程式庫支援「僅傳送一次」
支援的用戶端程式庫具有確認介面,可搭配回應使用 (例如 Go)。您可以使用這個介面,檢查確認要求是否成功。 如果確認要求成功,用戶端保證不會收到重新傳送的訊息。如果確認要求失敗,用戶端會重新傳送訊息。
用戶端也可以使用支援的用戶端程式庫,不必使用確認介面。不過,在這種情況下,確認失敗可能會導致系統在未通知的情況下重新傳送郵件。
支援的用戶端程式庫提供介面,可設定最短的租約延長時間 (例如:Go)。您必須將最低租約延長值設為較大的數字, 以免發生任何網路相關的確認逾期問題。 上限為 600 秒。
如果您使用 Java 用戶端程式庫,並透過
setChannelProvider()方法,使用自訂 gRPC 管道初始化訂閱者,建議您在建構TransportChannelProvider時,也將maxInboundMetadataSize設為至少 1 MB。如要進行這項設定,可以使用InstantiatingGrpcChannelProvider.Builder.setMaxInboundMetadataSize()或ManagedChannelBuilder.maxInboundMetadataSize()方法。
與「只傳送一次」相關的變數預設值和範圍,以及變數名稱,可能會因用戶端程式庫而異。舉例來說,在 Java 用戶端程式庫中,下列變數會控管「只傳送一次」的傳送作業。
| 變數 | 說明 | 值 |
|---|---|---|
setEnableExactlyOnceDelivery |
啟用或停用「僅傳送一次」傳遞。 | true 或 false 預設值=false |
minDurationPerAckExtension |
延長修改確認期限時,可使用的最短時間 (以秒為單位)。 | 範圍=0 至 600 預設值=無 |
maxDurationPerAckExtension |
延長修改確認期限時可用的最長時間 (以秒為單位)。 | 範圍=0 至 600 預設值=無 |
如果是「只傳送一次」傳送作業,當確認 ID 已過期時,對 Pub/Sub 的 modifyAckDeadline 或 acknowledgment 要求就會失敗。在這種情況下,服務會將過期的確認 ID 視為無效,因為較新的遞送作業可能已在進行中。這是為了確保訊息只傳送一次而設計的機制。接著,您會看到 acknowledgment 和 ModifyAckDeadline 要求傳回 INVALID_ARGUMENT 回應。如果停用「只傳送一次」功能,這些要求會在確認 ID 過期時傳回 OK。
為確保 acknowledgment 和 ModifyAckDeadline 要求具有有效的確認 ID,請考慮將 minDurationPerAckExtension 的值設為較大的數字。
區域性考量
只有在訂閱者連線至相同區域的服務時,系統才會保證僅傳送一次訊息。如果訂閱端應用程式分散在多個區域,即使啟用「僅傳送一次」,也可能導致重複傳送訊息。發布者可以將訊息傳送至任何區域,且系統仍會維持「僅傳送一次」的保證。
在 Google Cloud中執行應用程式時,應用程式預設會連線至同一區域的 Pub/Sub 端點。因此,在 Google Cloud內單一區域執行應用程式,通常可確保您與單一區域互動。
在 Google Cloud以外或多個區域執行訂閱端應用程式時,您可以在設定 Pub/Sub 用戶端時使用位置端點,確保連線至單一區域。Pub/Sub 的所有位置端點都指向單一區域。如要進一步瞭解位置端點,請參閱 Pub/Sub 端點。如需 Pub/Sub 的所有位置端點清單,請參閱「位置端點清單」。
建立「僅傳送一次」的訂閱項目
您可以使用 Google Cloud 控制台、Google Cloud CLI、用戶端程式庫或 Pub/Sub API,建立具有「只傳送一次」傳送方式的訂閱項目。
提取訂閱項目
控制台
如要建立具有「只傳送一次」傳送功能的提取訂閱項目,請按照下列步驟操作:
前往 Google Cloud 控制台的「Subscriptions」(訂閱項目) 頁面。
按一下「Create Subscription」 (建立訂閱項目)。
輸入訂閱 ID。
從下拉式選單中選擇或建立主題。
訂閱項目會接收來自主題的訊息。
在「僅傳送一次」部分,選取「啟用僅傳送一次」。
點選「建立」。
gcloud
如要建立採用「僅傳送一次」傳送方式的提取訂閱項目,請使用 gcloud pubsub subscriptions create 指令搭配 --enable-exactly-once-delivery 旗標:
gcloud pubsub subscriptions create SUBSCRIPTION_ID \ --topic=TOPIC_ID \ --enable-exactly-once-delivery
更改下列內容:
- SUBSCRIPTION_ID:要建立的訂閱項目 ID
- TOPIC_ID:要附加至訂閱項目的主題 ID
REST
如要建立僅傳送一次的訂閱項目,請使用 projects.subscriptions.create 方法。
PUT https://pubsub.googleapis.com/v1/projects/PROJECT_ID/subscriptions/SUBSCRIPTION_ID Authorization: Bearer $(gcloud auth print-access-token)
更改下列內容:
- PROJECT_ID:要在其中建立訂閱項目的專案 ID
- SUBSCRIPTION_ID:要建立的訂閱項目 ID
如要建立採用「僅傳送一次」傳遞方式的提取訂閱項目,請在要求主體中指定這項設定:
{ "topic": "projects/PROJECT_ID/topics/TOPIC_ID", "enableExactlyOnceDelivery": true, }
更改下列內容:
- PROJECT_ID:主題所在專案的專案 ID
- TOPIC_ID:要附加至訂閱項目的主題 ID
C++
在試用這個範例之前,請先按照「快速入門導覽課程:使用用戶端程式庫」中的 C++ 設定說明操作。詳情請參閱 Pub/Sub C++ API 參考文件。
C#
在試用這個範例之前,請先按照「快速入門導覽課程:使用用戶端程式庫」中的 C# 設定操作說明操作。詳情請參閱 Pub/Sub C# API 參考文件。
Go
下列範例使用 Go Pub/Sub 用戶端程式庫的主要版本 (v2)。如果您仍在使用第 1 版程式庫,請參閱第 2 版遷移指南。如要查看第 1 版程式碼範例清單,請參閱 已淘汰的程式碼範例。
在試用這個範例之前,請先按照「快速入門導覽課程:使用用戶端程式庫」中的 Go 設定說明操作。詳情請參閱 Pub/Sub Go API 參考文件。
Java
在試用這個範例之前,請先按照「快速入門導覽課程:使用用戶端程式庫」中的 Java 設定說明操作。詳情請參閱 Pub/Sub Java API 參考文件。
Python
在試用這個範例之前,請先按照「快速入門導覽課程:使用用戶端程式庫」中的 Python 設定說明操作。詳情請參閱 Pub/Sub Python API 參考文件。
Node.js
在試用這個範例之前,請先按照「快速入門導覽課程:使用用戶端程式庫」中的 Node.js 設定說明操作。詳情請參閱 Pub/Sub Node.js API 參考文件。
Node.js
在試用這個範例之前,請先按照「快速入門導覽課程:使用用戶端程式庫」中的 Node.js 設定說明操作。詳情請參閱 Pub/Sub Node.js API 參考文件。
小茹
下列範例使用 Ruby Pub/Sub 用戶端程式庫第 3 版。如果您仍在使用第 2 版程式庫,請參閱 第 3 版遷移指南。如要查看 Ruby 第 2 版程式碼範例清單,請參閱 已淘汰的程式碼範例。
在試用這個範例之前,請先按照「快速入門導覽課程:使用用戶端程式庫」中的 Ruby 設定操作說明操作。詳情請參閱 Pub/Sub Ruby API 參考文件。
PHP
在試用這個範例之前,請先按照「快速入門導覽課程:使用用戶端程式庫」中的 PHP 設定說明操作。詳情請參閱 Pub/Sub PHP API 參考文件。
Rust
在試用這個範例之前,請先按照「快速入門導覽課程:使用用戶端程式庫」中的 Rust 設定說明操作。詳情請參閱 Pub/Sub Rust API 參考文件。
監控「僅傳送一次」訂閱項目
這項指標會記錄可能導致重新遞送的事件數 (有效或重複)。
subscription/exactly_once_warning_count這項指標會計算 Pub/Sub 無法處理與確認 ID (ModifyAckDeadline 或 acknowledgment 要求) 相關聯要求的情況次數。失敗原因可能是伺服器或用戶端問題。舉例來說,如果用於維護「只傳送一次」資訊的持續性層無法使用,就會是伺服器型事件。如果用戶端嘗試使用無效的確認 ID 確認訊息,就會是基於用戶端的事件。
瞭解指標
subscription/exactly_once_warning_count 會擷取可能或可能不會導致實際重新遞送的事件,且可能會根據用戶行為產生雜訊。舉例來說,如果重複發出 acknowledgment 或 ModifyAckDeadline 要求,但確認 ID 無效,指標就會重複遞增。
下列指標也有助於瞭解用戶端行為:
subscription/expired_ack_deadlines_count指標會顯示確認 ID 過期的次數。確認 ID 過期可能會導致ModifyAckDeadline和acknowledgment要求失敗。service.serviceruntime.googleapis.com/api/request_count指標可用於擷取ModifyAckDeadline或acknowledgment要求的失敗情形,前提是要求已送達 Google Cloud ,但未送達 Pub/Sub。這項指標不會擷取某些失敗情況,例如用戶端與 Google Cloud中斷連線。
在大多數可重試的失敗事件中,支援的用戶端程式庫會自動重試要求。
配額
「僅一次」傳遞訂閱項目須遵守額外的配額規定。系統會對下列項目強制執行這些配額:
- 每個區域中,從啟用「僅傳送一次」傳送機制的訂閱項目取用的訊息數。
- 使用啟用「僅傳送一次」的區域訂閱項目時,已確認或延長期限的訊息數量。
如要進一步瞭解這些配額,請參閱「配額」主題中的表格。
僅傳送一次和依序傳送訂閱項目
Pub/Sub 支援依序傳送,確保訊息只會傳送一次。
使用「僅一次」傳遞機制時,Pub/Sub 會要求確認訊息依序傳送。如果確認順序有誤,服務會因暫時性錯誤而無法處理要求。如果確認期限到期,但用戶端尚未依序確認訊息已送達,系統會重新傳送訊息。因此,使用「只傳送一次」遞送機制進行排序時,用戶端處理量會限制在每秒數千則訊息。
「僅傳送一次」和推送訂閱項目
Pub/Sub 僅支援提取訂閱項目的「僅傳送一次」功能。
從推送訂閱項目取用訊息的用戶端,會以成功的回應回覆推送要求,藉此確認訊息。不過,用戶端不知道 Pub/Sub 訂閱項目是否收到並處理回應。這與提取訂閱不同,後者是由用戶端發起確認要求,如果要求處理成功,Pub/Sub 訂閱會做出回應。因此,「僅傳送一次」傳送語意與推送訂閱項目不太相符。
注意事項
如果在 CreateSubscription 時間未指定確認期限,啟用「僅傳送一次」的訂閱項目會將確認期限預設為 60 秒。
較長的預設確認期限有助於避免因網路事件而重新傳送訊息。支援的用戶端程式庫不會使用預設的訂閱項目確認期限。
與一般訂閱項目相比,「僅傳送一次」訂閱項目的發布端到訂閱端延遲時間會大幅增加。
如需高處理量,確切傳送一次的用戶端也必須使用串流提取。
即使啟用「僅傳送一次」,訂閱項目仍可能因發布端重複而收到多個相同訊息副本。發布端重複項目可能是因為發布用戶端或 Pub/Sub 服務多次重試發布作業所致。發布用戶端多次發布 (包括重試),會導致系統重新傳送不同 訊息 ID 的訊息。Pub/Sub 服務會發布多個不重複的訊息,以回應用戶端發布要求,因此會重新傳送相同的訊息 ID。
您可以在
subscription/exactly_once_warning_count中重試失敗的作業,支援的用戶端程式庫會自動重試這些作業。不過,如果失敗原因與無效的確認 ID 相關,則無法重試。