本頁說明如何使用 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」 (建立訂閱項目)。
輸入「Subscription ID」(訂閱項目 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
以下範例使用 Ruby Pub/Sub 用戶端程式庫 v3。如果您仍在使用第 2 版程式庫,請參閱 第 3 版遷移指南。如要查看 Ruby 第 2 版程式碼範例清單,請參閱 已淘汰的程式碼範例。
在試用這個範例之前,請先按照快速入門:使用用戶端程式庫的操作說明設定 Ruby 環境。詳情請參閱 Pub/Sub Ruby API 參考說明文件。
PHP
在試用這個範例之前,請先按照快速入門:使用用戶端程式庫中的 PHP 設定說明進行操作。 詳情請參閱 Pub/Sub PHP 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 訂閱會做出回應。因此,「僅傳送一次」傳送語意與推送訂閱項目不太相符。
注意事項
如果在建立訂閱項目時未指定確認期限,啟用「僅傳送一次」的訂閱項目會將確認期限預設為 60 秒。
較長的預設確認期限有助於避免因網路事件而重新傳送訊息。支援的用戶端程式庫不會使用預設的訂閱項目確認期限。
與一般訂閱項目相比,「僅傳送一次」訂閱項目的發布端到訂閱端延遲時間明顯較長。
如果需要高處理量,確切傳送一次的用戶端也必須使用串流提取。
即使啟用「僅傳送一次」,訂閱項目仍可能因發布端重複而收到多個相同訊息副本。發布端重複項目可能是因為發布用戶端或 Pub/Sub 服務多次重試發布作業所致。發布端用戶端在重試期間多次發布訊息,會導致系統重新傳送不同的訊息 ID。Pub/Sub 服務會多次發布不重複的訊息,以回應用戶端發布要求,導致系統重複傳送相同訊息 ID 的訊息。
您可以在
subscription/exactly_once_warning_count
中重試失敗的作業,支援的用戶端程式庫會自動重試這些作業。不過,如果失敗原因與無效的確認 ID 相關,則無法重試。