Reliable Services 生命週期
Reliable Services 是 Azure Service Fabric 提供的一種程式設計模型。 在認識 Reliable Services 的生命週期時,最需要了解的是基本的生命週期事件。 事件的確切順序取決於設定的詳細資料。
一般而言,Reliable Services 生命週期包含下列事件:
- 在啟動期間:
- 建構服務。
- 服務有機會建構和傳回零個以上的接聽程式。
- 開啟任何傳回的接聽程式,以便與服務進行通訊。
- 會呼叫服務的
runAsync
方法,以便服務能夠進行長時間執行的工作或背景工作。
- 在關閉期間:
- 會取消傳遞給
runAsync
的取消權杖,並關閉接聽程式。 - 解構服務物件本身。
- 會取消傳遞給
根據 Reliable Service 是無狀態或具狀態而定,Reliable Service 事件的順序可能稍有變化。
此外,對於具狀態服務,您必須處理主要交換案例。 在此序列期間,主要角色會轉移至另一個複本 (或回來),但服務不會關閉。
最後,您必須考慮錯誤或失敗狀況。
無狀態服務啟動
無狀態服務的生命週期相當簡單。 以下是事件的順序︰
- 建構服務。
- 叫用
StatelessService.createServiceInstanceListeners()
,並開啟任何傳回的接聽程式。 在每個接聽程式上呼叫CommunicationListener.openAsync()
。 - 然後平行執行:
- 呼叫服務的
runAsync
方法 (StatelessService.runAsync()
)。 - 如果有服務本身的
onOpenAsync
方法,會呼叫該方法。 具體來說,會呼叫StatelessService.onOpenAsync()
。 這是不常用的覆寫,但可用。
- 呼叫服務的
無狀態服務關閉
關閉無狀態服務時,也依循相同的模式,只是順序相反:
- 任何開啟的接聽程式皆為關閉。 在每個接聽程式上呼叫
CommunicationListener.closeAsync()
。 - 傳遞至
runAsync()
的取消權杖已取消。 檢查取消權杖的isCancelled
屬性傳回true
,還有呼叫權杖的throwIfCancellationRequested
方法是否擲回CancellationException
。 - 當完成
runAsync()
時,會呼叫服務的StatelessService.onCloseAsync()
方法 (若有)。 這同樣不是常見的覆寫,不過可以用來安全地關閉資源、停止背景處理、完成外部狀態儲存或關閉現有的連線。 StatelessService.onCloseAsync()
完成之後,就解構服務物件。
具狀態服務啟動
具狀態服務的模式類似於無狀態服務,只有一些不同。 針對啟動具狀態服務,事件的順序如下︰
- 建構服務。
- 呼叫
StatefulServiceBase.onOpenAsync()
。 這個呼叫在服務中不常覆寫。 - 叫用
StatefulServiceBase.createServiceReplicaListeners()
。- 如果服務是主要服務,則傳回的所有接聽程式為已開啟。 在每個接聽程式上呼叫
CommunicationListener.openAsync()
。 - 如果服務是次要服務,只有標示為
listenOnSecondary = true
的接聽程式才會開啟。 在次要上開啟的接聽程式較不常見。
- 如果服務是主要服務,則傳回的所有接聽程式為已開啟。 在每個接聽程式上呼叫
- 然後平行執行:
- 如果服務目前是主要,會呼叫此服務的
StatefulServiceBase.runAsync()
方法。 - 呼叫
StatefulServiceBase.onChangeRoleAsync()
。 這個呼叫在服務中不常覆寫。
- 如果服務目前是主要,會呼叫此服務的
注意
針對新的次要複本,會呼叫兩次 StatefulServiceBase.onChangeRoleAsync()
。 在步驟 2 之後,當其變成閒置次要複本,並在步驟 4 期間再次變成作用中次要複本時。 如需複本和執行個體生命週期的詳細資訊,請參閱複本和執行個體生命週期。
具狀態服務關閉
類似於無狀態服務,關閉期間的生命週期事件與啟動期間相同,但順序相反。 具狀態服務關閉時會發生下列事件︰
- 任何開啟的接聽程式皆為關閉。 在每個接聽程式上呼叫
CommunicationListener.closeAsync()
。 - 傳遞至
runAsync()
的取消權杖已取消。 對於取消權杖的isCancelled()
方法進行的呼叫傳回true
,還有呼叫權杖的throwIfCancellationRequested()
方法是否擲回OperationCanceledException
。 Service Fabric 等候runAsync()
完成。
注意
只有在此複本是主要複本時,才需要等候 runAsync
完成。
runAsync()
完成後,會呼叫服務的StatefulServiceBase.onCloseAsync()
方法。 此呼叫是不常用的覆寫,但可用。StatefulServiceBase.onCloseAsync()
完成之後,就解構服務物件。
具狀態服務主要複本互換
具狀態服務執行時,只有對於該具狀態服務的主要複本,才會開啟通訊接聽程式並呼叫 runAsync
方法。 建構次要複本,但看不到進一步的呼叫。 當具狀態服務執行時,哪個複本目前是主要可能會改變。 具狀態複本可看見的生命週期事件,取決於它在互換期間是降級或升級的複本。
針對降級的主要複本
Service Fabric 需要降級的主要複本停止處理訊息,並停止任何背景工作。 此步驟類似於關閉服務的情況。 差別在於服務仍為次要,並不會解構或關閉。 下列事件會發生:
- 任何開啟的接聽程式皆為關閉。 在每個接聽程式上呼叫
CommunicationListener.closeAsync()
。 - 傳遞至
runAsync()
的取消權杖已取消。 取消權杖的isCancelled()
方法之檢查會傳回true
。 如果呼叫,權杖的throwIfCancellationRequested()
方法會擲回OperationCanceledException
。 Service Fabric 等候runAsync()
完成。 - 標示為 listenOnSecondary = true 的接聽程式會開啟。
- 會呼叫服務的
StatefulServiceBase.onChangeRoleAsync()
。 這個呼叫在服務中不常覆寫。
針對升級的次要複本
同樣地,Service Fabric 需要升級的次要複本開始接聽網路上的訊息,並啟動其需要完成的任何背景工作。 此流程類似於建立服務的情況。 差別在於複本本身已經存在。 下列事件會發生:
- 會針對所有開啟的接聽程式 (標示為 listenOnSecondary = true) 呼叫
CommunicationListener.closeAsync()
- 會開啟所有通訊接聽程式。 在每個接聽程式上呼叫
CommunicationListener.openAsync()
。 - 然後平行執行:
- 呼叫服務的
StatefulServiceBase.runAsync()
方法。 - 呼叫
StatefulServiceBase.onChangeRoleAsync()
。 這個呼叫在服務中不常覆寫。
- 呼叫服務的
注意
只會呼叫一次 createServiceReplicaListeners
,而且不會在複本升級或降級流程期間再次呼叫;會使用相同的 ServiceReplicaListener
執行個體,但在關閉先前執行個體之後會建立新的 CommunicationListener
執行個體 (透過呼叫 ServiceReplicaListener.createCommunicationListener
方法)。
具狀態服務關閉和主要降級期間的常見問題
Service Fabric 基於多種原因變更具狀態服務的主要。 最常見的原因是叢集重新平衡和應用程式升級。 在這些作業期間,服務必須遵循 cancellationToken
。 這也適用於一般服務關閉的期間,例如刪除服務的情況。
未完全處理取消作業的服務將會遇到幾個問題。 這些作業可能會變慢,因為 Service Fabric 會以正常程序等待服務停止。 這可能最終會導致逾時的失敗升級和復原。 無法採用取消權杖也可能造成不平衡的叢集。 叢集會因為過於經常存取節點而失去平衡。 不過,無法重新平衡服務,因為將服務移至其他地方耗費太多時間。
因為這些服務是具有狀態的,所以服務會使用可靠集合。 在 Service Fabric 中,當主要降級時,首先發生的事情是撤銷基礎狀態的寫入存取權。 這會導致可能影響服務生命週期的第二組問題。 集合會根據時間和複本是否正在移動或關機而傳回例外狀況。 務必正確處理這些例外狀況。
Service Fabric 擲回的例外狀況都是永久 (FabricException
) 或暫時性 (FabricTransientException
)。 應該記錄並擲回永久例外狀況。 可以根據重試邏輯重試暫時性例外狀況。
測試和驗證 Reliable Services 時,請記得處理因將 ReliableCollections
而與服務生命週期事件搭配使用所導致的例外狀況,這是相當重要的一環。 建議您一律在負載下執行您的服務。 也應該先執行升級和混亂測試,再部署至生產環境。 下列基本步驟協助確保您的服務正確地實作,並正確地處理生命週期事件。
服務生命週期的注意事項
runAsync()
方法和createServiceInstanceListeners/createServiceReplicaListeners
呼叫都是選擇性。 服務可能會有其中一個或兩個,也可能完全沒有。 例如,若服務本身自行負責回應使用者呼叫,則不需要實作runAsync()
。 只需要通訊接聽程式及其相關聯的程式碼。 同樣地,建立和傳回通訊接聽程式是選擇性的。 該服務可能只有背景工作要進行,因此只需要實作runAsync()
。- 服務可以成功完成
runAsync()
並返回。 這不被視為失敗狀況。 這代表服務的背景工作正要完成。 對於具狀態 Reliable Services,如果服務從主要降級,然後又升級回到主要,則會再次呼叫runAsync()
。 - 如果服務藉由擲回某些非預期的例外狀況退出
runAsync()
,即是失敗。 服務物件會關閉,而且會報告健康情況錯誤。 - 雖然從這些方法進行傳回並沒有時間限制,您會立即喪失寫入的能力。 因此,您無法完成任何實際工作。 建議您盡快在收到取消要求後傳回。 如果您的服務未於合理的時間內回應這些 API 呼叫,Service Fabric 可能會強制終止服務。 這通常只發生在應用程式升級期間或刪除服務時。 此逾時預設為 15 分鐘。
onCloseAsync()
路徑的失敗會導致呼叫onAbort()
。 此呼叫是服務清除並釋放已宣告之任何資源的最後努力機會。 這個一般會在於節點上偵測到永久錯誤,或因內部失敗而 Service Fabric 無法可靠地管理服務執行個體生命週期時呼叫。- 具狀態服務複本變更角色 (例如變更為主要或次要角色) 時,會呼叫
OnChangeRoleAsync()
。 主要複本會獲得寫入狀態 (可建立並寫入可靠的集合)。 次要複本則會獲得讀取狀態 (只能從現有的可靠集合讀取)。 具狀態服務中的大部分工作會在主要複本執行。 次要複本可以執行唯讀驗證、產生報表、資料採礦或其他唯讀作業。