可靠的服務生命週期概觀

在考慮 Azure Service Fabric Reliable Services 的生命週期時,生命週期的基礎最重要。 一般情況下,生命週期包括下列各項:

  • 在啟動期間:
    • 建構服務。
    • 服務有機會建構和傳回零個以上的接聽程式。
    • 開啟任何傳回的接聽程式,能夠與服務進行通訊。
    • 會呼叫服務的 RunAsync 方法,以允許服務進行長時間執行的工作或背景工作。
  • 在關閉期間:
    • 會取消傳遞給 RunAsync 的取消權杖,並關閉接聽程式。
    • 接聽程式關閉之後,就解構服務物件本身。

這些事件的確切順序還有一些細節可討論。 根據 Reliable Service 是無狀態或具狀態而定,事件的順序可能稍有變化。 此外,對於具狀態服務,我們必須處理「主要」複本互換情況。 在此序列期間,「主要」角色會轉移至另一個複本 (或回來),但服務不會關閉。 最後,我們必須考慮錯誤或失敗狀況。

無狀態服務啟動

無狀態服務的生命週期相當簡單。 以下是事件的順序︰

  1. 建構服務。
  2. 叫用 StatelessService.CreateServiceInstanceListeners(),並開啟任何傳回的接聽程式。 在每個接聽程式上呼叫 ICommunicationListener.OpenAsync()
  3. 接著,兩件事平行發生︰
    • 呼叫服務的 StatelessService.RunAsync() 方法。
    • 如果有的話,會呼叫服務的 StatelessService.OnOpenAsync() 方法。 此呼叫是不常用的覆寫,但可用。 這個階段可以啟動擴充服務的初始化作業。

無狀態服務關閉

針對關閉無狀態服務,也依循相同的模式,只是順序相反:

  1. 任何開啟的接聽程式皆為關閉。 在每個接聽程式上呼叫 ICommunicationListener.CloseAsync()
  2. 傳遞至 RunAsync() 的取消權杖已取消。 檢查取消權杖的 IsCancellationRequested 屬性傳回 true,還有呼叫權杖的 ThrowIfCancellationRequested 方法是否擲回 OperationCanceledException。 Service Fabric 等候 RunAsync() 完成。
  3. RunAsync() 完成後,會呼叫服務的 StatelessService.OnCloseAsync() 方法 (如果有的話)。 無狀態服務執行個體即將正常關閉時,會呼叫 OnCloseAsync。 這可能在升級服務程式碼、因負載平衡而移動服務執行個體或偵測到暫時性失敗的時侯發生。 覆寫 StatelessService.OnCloseAsync() 的情況並不常見,不過可以用來安全地關閉資源、停止背景處理、完成外部狀態儲存,或關閉現有的連線。
  4. StatelessService.OnCloseAsync() 完成之後,就解構服務物件。

具狀態服務啟動

具狀態服務的模式類似於無狀態服務,只有一些不同。 針對啟動具狀態服務,事件的順序如下︰

  1. 建構服務。

  2. 呼叫 StatefulServiceBase.OnOpenAsync()。 這個呼叫在服務中不常覆寫。

  3. 叫用 StatefulServiceBase.CreateServiceReplicaListeners()

    • 如果服務是「主要」服務,則傳回的所有接聽程式為已開啟。 在每個接聽程式上呼叫 ICommunicationListener.OpenAsync()
    • 如果服務是「次要」服務,只有標示為 ListenOnSecondary = true 的接聽程式才會開啟。 在次要上開啟的接聽程式較不常見。
  4. 然後平行執行:

    • 如果服務目前是「主要」,會呼叫此服務的 StatefulServiceBase.RunAsync() 方法。
    • 呼叫 StatefulServiceBase.OnChangeRoleAsync()。 這個呼叫在服務中不常覆寫。

    注意

    針對新的次要複本,會呼叫兩次 StatefulServiceBase.OnChangeRoleAsync()。 在步驟 2 之後,當其變成閒置次要複本,並在步驟 4 期間再次變成作用中次要複本時。 如需複本和執行個體生命週期的詳細資訊,請參閱複本和執行個體生命週期

具狀態服務關閉

類似於無狀態服務,關閉期間的生命週期事件與啟動期間相同,但順序相反。 具狀態服務關閉時會發生下列事件︰

  1. 任何開啟的接聽程式皆為關閉。 在每個接聽程式上呼叫 ICommunicationListener.CloseAsync()

  2. 會呼叫 StatefulServiceBase.OnCloseAsync() 方法。 此呼叫是不常用的覆寫,但可用。

  3. 傳遞至 RunAsync() 的取消權杖已取消。 檢查取消權杖的 IsCancellationRequested 屬性傳回 true,還有呼叫權杖的 ThrowIfCancellationRequested 方法是否擲回 OperationCanceledException。 Service Fabric 等候 RunAsync() 完成。

    注意

    只有當此複本是「主要」複本時,才需要等候 RunAsync 完成。

  4. StatefulServiceBase.RunAsync() 完成之後,就解構服務物件。

具狀態服務主要複本互換

當具狀態服務執行時,只有此具狀態服務的「主要」複本會開啟通訊接聽程式和呼叫 RunAsync 方法。 建構次要複本,但看不到進一步的呼叫。 當具狀態服務正在執行時,目前的主要複本可能因為錯誤或叢集平衡最佳化而變更。 就複本可見的生命週期事件來說,這代表什麼? 具狀態複本所看到的行為,取決於它在互換期間是降級或升級的複本。

降級的主要複本

針對降級的主要複本,Service Fabric 需要此複本停止處理訊息並結束它正在進行的任何背景工作。 因此,這個步驟類似於服務關閉時的情形。 差別在於服務仍為「次要」,並不會解構或關閉。 呼叫下列 API:

  1. 任何開啟的接聽程式皆為關閉。 在每個接聽程式上呼叫 ICommunicationListener.CloseAsync()
  2. 傳遞至 RunAsync() 的取消權杖已取消。 檢查取消權杖的 IsCancellationRequested 屬性傳回 true,還有呼叫權杖的 ThrowIfCancellationRequested 方法是否擲回 OperationCanceledException。 Service Fabric 等候 RunAsync() 完成。
  3. 標示為 ListenOnSecondary = true 的接聽程式會開啟。
  4. 會呼叫服務的 StatefulServiceBase.OnChangeRoleAsync()。 這個呼叫在服務中不常覆寫。

升級的次要複本

同樣地,Service Fabric 需要升級的次要複本開始接聽網路上的訊息,並啟動其需要完成的任何背景工作。 因此,這個程序類似於建立服務時的情形,差別在於複本它本身已存在。 呼叫下列 API:

  1. 會針對所有開啟的接聽程式 (標示為 ListenOnSecondary = true) 呼叫 ICommunicationListener.CloseAsync()
  2. 會開啟所有通訊接聽程式。 在每個接聽程式上呼叫 ICommunicationListener.OpenAsync()
  3. 然後平行執行:
    • 呼叫服務的 StatefulServiceBase.RunAsync() 方法。
    • 呼叫 StatefulServiceBase.OnChangeRoleAsync()。 這個呼叫在服務中不常覆寫。

注意

只會呼叫一次 CreateServiceReplicaListeners,而且不會在複本升級或降級流程期間再次呼叫;會使用相同的 ServiceReplicaListener 執行個體,但在關閉先前執行個體之後會建立新的 ICommunicationListener 執行個體 (透過呼叫 ServiceReplicaListener.CreateCommunicationListener 方法)。

具狀態服務關閉和主要降級期間的常見問題

Service Fabric 基於各種原因變更具狀態服務的「主要」。 最常見的是叢集重新平衡應用程式升級。 在這些作業期間 (以及在正常服務關機期間,如您在刪除服務時所見),服務遵守 CancellationToken 是相當重要的。

未完全處理取消作業的服務將會遇到幾個問題。 這些作業可能會變慢,因為 Service Fabric 會以正常程序等待服務停止。 這可能最終會導致逾時的失敗升級和復原。 無法採用取消權杖也可能造成不平衡的叢集。 叢集變成不平衡,因為節點變成經常性存取,但無法重新平衡服務,因為將服務移至其他地方耗費太多時間。

因為這些服務是具有狀態的,所以它們會使用可靠集合。 在 Service Fabric 中,當「主要」降級時,首先發生的事情是撤銷基礎狀態的寫入存取權。 這會導致可能影響服務生命週期的第二組問題。 集合會根據時間和複本是否正在移動或關機而傳回例外狀況。 應該正確處理這些例外狀況。 Service Fabric 擲回的例外狀況可分為永久性 (FabricException) 和暫時性(FabricTransientException) 類別。 永久性例外狀況應該要記錄並擲回,而暫時性例外狀況可能會根據一些重試邏輯來重試。

處理由於使用與服務生命週期事件搭配使用的 ReliableCollections 而造成的例外況況,是測試和驗證可靠服務的重要環節。 建議一律先在低於負載的情況下執行服務,同時執行升級和混亂測試,然後再部署到生產。 下列基本步驟協助確保您的服務正確地實作,並正確地處理生命週期事件。

服務生命週期的注意事項

  • RunAsync() 方法和 CreateServiceReplicaListeners/CreateServiceInstanceListeners 呼叫都是選擇性。 一個服務可能具有其中一個、兩者或都沒有。 例如,若服務本身自行負責回應使用者呼叫,則不需要實作 RunAsync()。 只需要通訊接聽程式及其相關聯的程式碼。 同樣地,建立和傳回通訊接聽程式是選擇性,因為服務可能只有背景工作要處理,所以只需要實作 RunAsync()
  • 服務可以成功完成 RunAsync() 並返回。 完成不是失敗情況。 完成 RunAsync() 表示服務的背景工作已完成。 對於具狀態可靠服務,如果複本從「主要」降級到「次要」,然後又升級回到「主要」,則會再次呼叫 RunAsync()
  • 如果服務藉由擲回某些非預期的例外狀況退出 RunAsync(),則會構成失敗。 服務物件會關閉,而且會報告健康情況錯誤。
  • 雖然從這些方法傳回沒有時間限制,但您會立即喪失寫入到可靠的集合的能力,並因此無法完成任何實際工作。 建議您盡快在收到取消要求後傳回。 如果您的服務未於合理的時間內回應這些 API 呼叫,Service Fabric 可能會強制終止服務。 這通常只發生在應用程式升級期間或刪除服務時。 此逾時預設為 15 分鐘。
  • OnCloseAsync() 路徑失敗會導致呼叫 OnAbort(),這是服務清除並釋放已宣告之任何資源的最後努力機會。 這個一般會在於節點上偵測到永久錯誤,或因內部失敗而 Service Fabric 無法可靠地管理服務執行個體生命週期時呼叫。
  • 具狀態服務複本變更角色 (例如變更為主要或次要角色) 時,會呼叫 OnChangeRoleAsync()。 主要複本會獲得寫入狀態 (可建立並寫入可靠的集合)。 次要複本則會獲得讀取狀態 (只能從現有的可靠集合讀取)。 具狀態服務中的大部分工作會在主要複本執行。 次要複本可以執行唯讀驗證、產生報表、資料採礦或其他唯讀作業。

下一步