Reliable Services のライフサイクル
Reliable Services は、Azure Service Fabric で利用できるプログラミング モデルの 1 つです。 Reliable Services のライフサイクルについて知ろうとするときは、基本的なライフサイクル イベントを理解することが最も重要です。 イベントの厳密な順序は、構成の詳細によって異なります。
一般に、Reliable Services のライフサイクルには次のイベントが含まれます。
- スタートアップ時
- サービスが構築されます。
- サービスは 0 個以上のリスナーを構築して返すことができます。
- 返されたリスナーはすべて、サービスとの通信のために開かれます。
- サービスの
runAsync
メソッドが呼び出されて、長時間実行される作業やバックグラウンド作業を行うことができるようになります。
- シャットダウン時
runAsync
に渡されたキャンセル トークンが取り消されて、リスナーが閉じられます。- サービス オブジェクト自体が破棄されます。
Reliable Services でのイベントの順序は、リライアブル サービスがステートレスかステートフルかによって若干異なる場合があります。
また、ステートフル サービスでは、プライマリ スワップ シナリオに対処する必要があります。 このシーケンスでは、サービスがシャットダウンすることなく、プライマリのロールが別のレプリカに転送 (または別のレプリカから復帰) されます。
最後に、エラーまたは障害の状況を考慮する必要があります。
ステートレス サービスのスタートアップ
ステートレス サービスのライフサイクルは、非常にわかりやすいものです。 イベントの順序は次の通りです。
- サービスが構築されます。
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()
が呼び出されます この呼び出しがサービスでオーバーライドされることはほとんどありません。
- サービスが現在プライマリである場合、サービスの
Note
新しいセカンダリ レプリカの場合は、StatefulServiceBase.onChangeRoleAsync()
が 2 回呼び出されます。 アイドル状態のセカンダリになる手順 2 の後で 1 回と、アクティブ セカンダリになる手順 4 の間にもう 1 回です。 レプリカとインスタンスのライフサイクルの詳細については、レプリカとインスタンスのライフサイクルに関するページをお読みください。
ステートフル サービスのシャットダウン
ステートレス サービスと同様に、シャットダウン中のライフサイクル イベントは起動時と同じですが、順序が逆です。 ステートフル サービスのシャットダウン時には、次のイベントが発生します。
- 開いているすべてのリスナーが閉じられます。
CommunicationListener.closeAsync()
が各リスナーに呼び出されます。 runAsync()
に渡されたキャンセル トークンが取り消されます。 キャンセル トークンのisCancelled()
メソッドを呼び出すとtrue
が返され、メソッドを呼び出した場合、トークンのthrowIfCancellationRequested()
メソッドはOperationCanceledException
をスローします。 Service Fabric はrunAsync()
が完了するまで待機します。
Note
runAsync
の完了の待機は、このレプリカがプライマリ レプリカである場合にのみ必要です。
runAsync()
が完了すると、サービスのStatefulServiceBase.onCloseAsync()
メソッドが呼び出されます。 この呼び出しのオーバーライドは一般的ではありませんが利用可能です。StatefulServiceBase.onCloseAsync()
が完了すると、サービス オブジェクトは破棄されます。
ステートフル サービスのプライマリ スワップ
ステートフル サービスの実行中、通信リスナーは開かれていて、runAsync
メソッドはそのステートフル サービスのプライマリ レプリカに対してだけ呼び出されます。 セカンダリ レプリカは構築されますが、それ以上の呼び出しは行われません。 ステートフル サービスの実行中に、現在プライマリであるレプリカが変更される場合があります。 ステートフル レプリカが認識できるライフサイクル イベントは、それがスワップ中に降格されているレプリカか、昇格されているレプリカかによって異なります。
降格されたプライマリの場合
Service Fabric では、メッセージの処理およびすべてのバックグラウンド処理を停止するために、降格されたプライマリ レプリカが必要です。 このステップは、サービスがシャットダウンされるときに似ています。 1 つ違う点は、サービスがセカンダリのままであるために、サービスが破棄されたり閉じられたりしないということです。 次のイベントが発生します。
- 開いているすべてのリスナーが閉じられます。
CommunicationListener.closeAsync()
が各リスナーに呼び出されます。 runAsync()
に渡されたキャンセル トークンが取り消されます。 キャンセル トークンのisCancelled()
メソッドをチェックすると、true
が返ります トークンのthrowIfCancellationRequested()
メソッドを呼び出すと、OperationCanceledException
がスローされます。 Service Fabric はrunAsync()
が完了するまで待機します。- listenOnSecondary = true としてマークされたリスナーが開かれます。
- サービスの
StatefulServiceBase.onChangeRoleAsync()
が呼び出されます。 この呼び出しがサービスでオーバーライドされることはほとんどありません。
昇格されたセカンダリの場合
同様に、Service Fabric では、メッセージのリッスンを開始し、完了する必要があるバックグラウンド タスクを開始するために、昇格されたセカンダリ レプリカが必要です。 このプロセスは、サービスが作成されるときに似ています。 違いは、レプリカ自体が既に存在していることです。 次のイベントが発生します。
CommunicationListener.closeAsync()
は、開いているすべてのリスナー (listenOnSecondary = true でマークされている) に対して呼び出されます- すべての通信リスナーが開かれます。
CommunicationListener.openAsync()
が各リスナーに呼び出されます。 - 次に、次のことが並行して行われます。
- サービスの
StatefulServiceBase.runAsync()
メソッドが呼び出されます。 StatefulServiceBase.onChangeRoleAsync()
が呼び出されます この呼び出しがサービスでオーバーライドされることはほとんどありません。
- サービスの
Note
createServiceReplicaListeners
は 1 回だけ呼び出され、レプリカの昇格または降格プロセス中に再び呼び出されることはありません。同じ ServiceReplicaListener
インスタンスが使用されますが、前のインスタンスが閉じられた後に (ServiceReplicaListener.createCommunicationListener
メソッドを呼び出すことによって) 新しい CommunicationListener
インスタンスが作成されます。
ステートフル サービスのシャットダウンとプライマリの降格における一般的な問題
Service Fabric は、複数の理由でステートフル サービスのプライマリを変更します。 最も一般的な理由は、クラスターの再分散とアプリケーションのアップグレードです。 これらの操作では、サービスが cancellationToken
に従うことが重要です。 これは、サービスが削除された場合など、サービスの通常のシャットダウンにも適用されます。
取り消しを適切に処理しないサービスでは、いくつかの問題が発生する可能性があります。 サービスが正常に停止するのを Service Fabric が待機するため、それらの操作の速度が遅くなります。 最終的には、アップグレードがタイムアウトしてロールバックし、アップグレードが失敗する可能性があります。 キャンセル トークンに従わないと、クラスターが不均衡になる場合もあります。 クラスターが不均衡になる原因は、ノードの負荷が高くなっても、 サービスの移動に時間がかかるので、サービスの分散を調整できないためです。
サービスがステートフルなため、サービスが Reliable Collection を使う可能性も高くなります。 Service Fabric では、プライマリが降格されたときに最初に発生することの 1 つは、基になる状態への書き込みアクセスの取り消しです。 これは、サービスのライフサイクルに影響を与える第 2 の一連の問題につながります。 レプリカが移動中なのかあるいはシャットダウンされているのかと、そのタイミングに基づいて、コレクションが例外を返します。 これらの例外を正しく処理することが重要です。
Service Fabric によってスローされる例外には、永続的なもの (FabricException
) と一時的なもの (FabricTransientException
) があります。 永続的な例外は、ログに記録されてスローされる必要があります。 一時的な例外は、再試行ロジックに基づいて再試行できます。
Reliable Services のテストと検証の重要な部分は、ReliableCollections
とサービスのライフサイクル イベントを組み合わせて使ったことによる例外を処理することです。 負荷がかかった状態で常にサービスを実行することをお勧めします。 また、運用環境にデプロイする前に、アップグレードと混乱テストを実行する必要もあります。 これらの基本手順を行うことで、サービスが正しく実装され、ライフサイクル イベントが正しく処理されるようになります。
サービスのライフサイクルに関するメモ
runAsync()
メソッドおよびcreateServiceInstanceListeners/createServiceReplicaListeners
の呼び出しは両方とも省略可能です。 サービスでは、どちらか一方だけまたは両方を使うか、どちらも使わない場合があります。 たとえば、サービスがユーザーの呼び出しに応答してすべての処理を実行する場合、runAsync()
を実装する必要はありません。 通信リスナーおよびそれに関連付けられたコードのみが必要です。 同様に、通信リスナーを作成して返すことも省略可能です。 サービスで行うのがバックグラウンド処理だけの場合、runAsync()
を実装することだけが必要です。- サービスは、正常に
runAsync()
を完了して戻ることができます。 これは障害状態とは見なされません。 これは、サービスのバックグラウンド処理の終了を表します。 ステートフル リライアブル サービスでは、サービスがプライマリから一度降格して再度プライマリに昇格した場合、runAsync()
が再度呼び出されます。 - サービスが予期しない例外をスローして
runAsync()
を終了した場合はエラーです。 サービス オブジェクトがシャットダウンされ、正常性のエラーが報告されます。 - これらのメソッドから制御が戻るときに時間制限はありませんが、書き込みはすぐにできなくなります。 そのため、実際の処理を完了できません。 取り消し要求を受信したらできるだけ短時間で制御を戻すことをお勧めします。 サービスが適切な時間内にこれらの API 呼び出しに応答しない場合、Service Fabric はサービスを強制的に終了する場合があります。 通常、これはアプリケーションのアップグレード中か、サービスの削除中のみに発生します。 このタイムアウト時間は既定で 15 分です。
onCloseAsync()
のパスで障害が起きると、onAbort()
が呼び出されます。 これは、サービスが要求したリソースすべてをクリーンアップし解放するための最後のベストエフォートの機会になります。 これは一般に、ノードで永続的なエラーが検出されたときや Service Fabric が内部エラーのために、サービス インスタンスのライフ サイクルを確実に管理できないときに呼び出されます。OnChangeRoleAsync()
は、ステートフル サービス レプリカのロールが、プライマリやセカンダリなどに変更されるときに呼び出されます。 プライマリ レプリカには書き込み状態が与えられます (Reliable Collection の作成と Reliable Collection への書き込みが可能)。 セカンダリ レプリカには読み取り状態が与えられます (既存の Reliable Collection からの読み取りのみが可能)。 ステートフル サービスの作業のほとんどは、プライマリ レプリカで実行されます。 セカンダリ レプリカでは、読み取り専用の検証、レポートの生成、データ マイニングなど、読み取り専用のジョブを実行できます。