Azure Functions 可靠的事件處理

事件處理是與無伺服器架構相關聯的其中一種最常見案例。 本文說明如何使用 Azure Functions 來建立可靠的訊息處理器,以避免遺失訊息。

分散式系統中事件串流的挑戰

請考慮使用以每秒 100 個事件的固定速率傳送事件的系統。 使用此速率,在幾分鐘內,多個平行 Functions 執行個體每秒可以使用連入的 100 個事件。

不過,可能會發生下列任何較不理想的狀況:

  • 如果事件發行者傳送損毀事件,該怎麼辦?
  • 如果您的 Functions 執行個體遇到未處理的例外狀況,該怎麼辦?
  • 如果下游系統離線,該怎麼辦?

如何處理這些情況,同時保留應用程式的輸送量?

使用佇列時,會自然地進行可靠的傳訊。 與 Functions 觸發程序配對時,函數會在佇列訊息上建立鎖定。 如果處理失敗,則會釋放鎖定,以允許另一個執行個體重試處理。 接著,除非成功評估訊息,或將訊息新增至有害佇列,否則會繼續處理。

即使單一佇列訊息可以在重試週期予以保留,其他平行執行還是會繼續清除佇列中的其餘訊息。 結果是一個不正確的訊息並不會大幅影響整體輸送量。 不過,儲存體佇列不保證順序,也不會針對事件中樞所需的高輸送量需求進行最佳化。

相比之下,Azure 事件中樞未包括鎖定概念。 為了允許高輸送量、多個取用者群組和重新執行功能這類功能,事件中樞事件的行為更像視訊播放程式。 事件會從每個分割區串流中的單一點進行讀取。 從該指標,您可以從該位置向前或向後讀取,但必須選擇移動要處理的事件的指標。

串流中發生錯誤時,如果您決定將指標保留在相同的位置,則除非指標前進,否則會封鎖事件處理。 換句話說,如果指標停止以處理在處理單一事件時發生的問題,則未處理的事件會開始向上移轉。

不論成功或失敗,Azure Functions 都會推進串流的指標,以避免死結。 因為指標持續前進,所以您的函數需要適當地處理失敗。

Azure Functions 取用事件中樞事件的方式

Azure Functions 會在循環執行下列步驟時取用事件中樞事件:

  1. 指標是針對事件中樞的每個分割區所建立,並持續保存至 Azure 儲存體。
  2. 接收到新訊息時 (預設為批次),主機會嘗試使用訊息批次來觸發函數。
  3. 如果函數完成執行 (不管有沒有例外狀況),則指標會前進,並將檢查點儲存至儲存體帳戶。
  4. 如果條件導致函數執行無法完成,則主機無法讓指標前進。 如果指標未前進,則稍後的檢查最後會處理相同的訊息。
  5. 重複步驟 2-4

此行為揭示一些重點:

  • 未處理的例外狀況可能會導致您遺失訊息。 導致例外狀況的執行將會繼續讓指標前進。 設定重試原則將會延遲指標的進度,直到評估完整個重試原則為止。
  • Functions 保證至少傳遞一次。 您的程式碼和相依系統可能需要考慮相同的訊息可能會收到兩次的事實

處理例外狀況

一般來說,每個函數都應該在最高層級的程式碼中包括 try/catch 區塊。 具體而言,所有使用事件中樞事件的函數都應該具有 catch 區塊。 如此一來,引發例外狀況時,catch 區塊會在指標前進之前處理錯誤。

重試機制和原則

某些例外狀況本質上是暫時性的,而且在稍後重試作業時不會再次出現。 這是第一個步驟一律為重試作業的原因。 您可以利用函數應用程式重試原則,或撰寫函數執行內的重試邏輯。

將錯誤處理行為引進函數,可讓您定義基本和進階重試原則。 例如,您可以實作遵循下列規則所述工作流程的原則:

  • 嘗試插入訊息三次 (重試之間可能會發生延遲)。
  • 如果所有重試的最終結果都失敗,則請將訊息新增至佇列,讓處理可以在串流上繼續。
  • 稍後會處理損毀或未處理的訊息。

注意

Polly 是 C# 應用程式的復原和暫時性錯誤處理程式庫範例。

非例外狀況錯誤

即使錯誤不存在,還是會發生一些問題。 例如,請考慮執行中間發生的失敗。 在此情況下,如果函數未完成執行,則偏移指標永遠不會前進。 如果指標未前進,則失敗執行之後所執行的任何執行個體都會繼續讀取相同的訊息。 這種情況提供「至少一次」保證。

每個訊息至少處理一次的保證,表示某些訊息可能處理多次。 您的函數應用程式需要注意這種可能性,而且必須以等冪性準則為基礎來建置。

停止並重新啟動執行

雖然可以接受少量錯誤,但如果您的應用程式遇到大量失敗,該怎麼辦? 除非系統達到狀況良好狀態,否則您可能會想要停止觸發事件。 具有暫停處理的機會通常是使用斷路器模式所達成。 斷路器模式可讓您的應用程式「中斷」事件程序,並在稍後繼續。

在事件程序中實作斷路器需要兩個部分:

  • 所有執行個體的共用狀態,以追蹤和監視線路的健康情況
  • 可管理線路狀態 (開啟或關閉) 的主要程序

實作詳細資料可能會不同,但會在您需要儲存體機制的執行個體之間共用狀態。 您可以選擇將狀態儲存至 Azure 儲存體、Redis 快取或函數集合可存取的任何其他帳戶。

Azure Logic Apps耐久函數本質上就適合用來管理工作流程和線路狀態。 其他服務可能也會正常運作,但此範例會使用邏輯應用程式。 使用邏輯應用程式,您可以暫停並重新啟動函數的執行,讓您能夠擁有實作斷路器模式所需的控制權。

定義跨執行個體的失敗閾值

若要考量多個同時處理事件的執行個體,需要持續保存共用的外部狀態,才能監視線路的健康情況。

您可以選擇實作的規則可能會強制執行:

  • 如果所有執行個體在 30 秒內有超過 100 個最終失敗,則請中斷線路,並停止在新訊息上觸發。

實作詳細資料會隨著您的需求而不同,但一般而言,您可以建立一個系統,以:

  1. 記錄儲存體帳戶 (Azure 儲存體、Redis 等) 的失敗
  2. 記錄新的失敗時,請檢查滾動計數,以查看閾值是否相符 (例如,在過去 30 秒超過 100 個)。
  3. 如果達到閾值,則請向 Azure 事件方格發出事件,告知系統中斷線路。

使用 Azure Logic Apps 管理線路狀態

下列描述醒目提示建立 Azure Logic App 以停止處理 Functions 應用程式的一種方式。

Azure Logic Apps 隨附不同服務的內建連接器、具備具狀態協調流程,而且是管理線路狀態的自然選擇。 偵測到線路需要中斷之後,您可以建置邏輯應用程式來實作下列工作流程:

  1. 觸發事件方格工作流程,並停止 Azure Function (使用 Azure 資源連接器)
  2. 傳送包含工作流程重新啟動選項的通知電子郵件

電子郵件收件者可以調查線路的健康情況,並在適當情況下,透過通知電子郵件中的連結來重新啟動線路。 工作流程重新啟動函數時,會從最後一個事件中樞檢查點處理訊息。

使用此方式時,不會遺失任何訊息、會依序處理所有訊息,而且您可以視需要中斷線路。

資源

下一步

如需詳細資訊,請參閱以下資源: