1. 概要
MSA(Microservice Architecture)ベースのアプリケーションは、1つの巨大なシステムを複数の独立したサービスに分割して構成するアーキテクチャです。各サービスは異なる機能と責任を持ちますが、実際にサービスを提供するためには、サービス間でのデータ連携と状態共有を継続的に行う必要があります。サービス間通信の方式の1つとしてメッセージング方式が使用され、非同期のデータ転送が必要な場合はメッセージブローカー(Message Broker)を利用できます。
Aプロジェクトでは、船舶内の複数のサービス間でセンサーデータやイベントデータを安定的かつ効率的に転送するため、NATSベースの永続メッセージングシステムであるNATS JetStreamを使用しています。NATS JetStreamは、サービス間の非同期メッセージ転送だけでなく、メッセージの保存、Consumerの状態管理、再送などの機能を提供し、継続的に発生する船舶データを安定して処理できるよう支援します。
しかし、実際の開発および運用過程では、船舶サーバーの強制終了やアプリケーションの異常終了など、さまざまな状況が発生します。そのような環境で、NATS JetStreamに関連する予期しない障害を頻繁に経験しました。本稿では、NATS JetStreamの運用中に発生した障害事例を基に主な発生原因を分析し、今後同様の障害が発生した場合に、Consumerの状態値を基に原因を特定して対応できるよう、診断手順を整理します。
2. メッセージ処理障害の発生
サーバーが強制終了されるなど、異常終了した後に再起動された際、Streamにはメッセージが継続的にPublishされていたにもかかわらず、Durable Consumerがそれらのメッセージを正常に消費できない現象が発生しました。
本アプリケーションは、起動時に既存のDurable Consumerの有無を確認し、すでにConsumerが存在する場合は新たに作成せず、既存のConsumerをそのまま再利用するよう実装されています。Durable Consumerは単なる購読情報だけでなく、メッセージがどこまで配信されたかを示すDelivered Sequence、Ackが完了した位置を示すAck Floor、まだAckされていないメッセージの状態、再送対象のメッセージなど、消費状態を継続的に保持します。そのため、メッセージの処理中にサーバーが強制終了されると、一部メッセージのAck処理が完了しなかったり、Consumerの配信および処理状態が正常に完了しないまま残ったりする可能性があります。
サーバーの再起動後、アプリケーションはこの既存のDurable Consumerをそのまま再利用するため、異常終了直前に残っていたメッセージ処理状態も引き継ぐことになります。正常な場合は、AckWaitと再送ポリシーに従って未処理メッセージが再度配信され、その後もメッセージの消費が継続されるはずです。しかし、Ack Pendingメッセージが蓄積したり、Delivered SequenceとAck Floorの間の処理状態が正常に引き継がれなかったりするなど、さまざまなケースによって、Consumerによるメッセージ配信が遅延または停止することがあります。
3. Consumerの状態値を分析する方法
障害が発生した場合は、まずConsumerの状態情報を確認し、どの段階で障害が発生したのかを把握する必要があります。
Information for Consumer “stream” > “consumer”
Configuration:
Name: “consumer”
Pull Mode: true
Filter Subject: subject.>
Deliver Policy: All
Ack Policy: Explicit
Ack Wait: 30s
Maximum Deliveries: 3
Maximum Ack Pending: 1000
State:
Last Delivered Message:
Consumer sequence: 15230
Stream sequence: 185400
Acknowledgment Floor:
Consumer sequence: 14230
Stream sequence: 184400
OutStanding Acks: 1000
Redelivered Messages: 15
Unprocessed Messages: 3270
Waiting Pulls: 1
|
項目 |
意味 |
確認事項 |
|---|---|---|
|
Last Delivered Message |
Consumerがどこまでメッセージを配信したか |
特定のSequenceで長時間停止していないか確認 |
|
Acknowledgment Floor |
連続してどこまでAck処理が完了したか |
Last Deliveredとの差が過度に大きくないか確認 |
|
OutStanding Acks |
配信済みだが、まだAckされていないメッセージ数 |
Maximum Ack Pending値に到達していないか確認 |
|
Unprocessed Messages |
Consumerにまだ配信されていないメッセージ数 |
0より大きいにもかかわらず消費が進んでいないか確認 |
|
Redelivered Messages |
再送されたメッセージ数 |
サーバー再起動後に異常に増加していないか確認 |
|
Waiting Pulls |
現在メッセージを要求して待機中のPullリクエスト数 |
0の場合、アプリケーションのPullループが動作していない可能性を確認 |
-
表1. Consumerの主な状態値と意味
1) OutStanding AcksがMaxAckPendingに到達した場合
OutStanding Acksは、Consumerがアプリケーションに配信したものの、まだAckを受け取っていないメッセージ数を意味します。この値がMaxAckPendingの設定値に到達すると、JetStreamはAckが処理されて空きができるまで、新しいメッセージの配信を制限することがあります。
Maximum Ack Pending: 1000
OutStanding Acks: 1000
Unprocessed Messages: 3270
上記の例では、Streamにまだ3270個のメッセージが残っていますが、Consumerはすでに許可されたAck Pendingの上限までメッセージを配信した状態です。そのため、アプリケーションでAckが進まない場合、新しいメッセージがそれ以上配信されず、外部からのPublishは正常なのにConsumerはまったくメッセージを受信できない現象が発生する可能性があります。
2) Pullリクエストが正常に発生しない場合
Pull Consumerでは、アプリケーションがfetch()またはconsume()方式でPullリクエストを送信して初めてメッセージが配信されます。そのため、配信対象のメッセージが残っているにもかかわらずWaiting Pullsが継続して0である場合は、Consumer自体よりもアプリケーションのPull処理ロジックを疑うことができます。
Unprocessed Messages: 3270
OutStanding Acks: 0
Waiting Pulls: 0
この状態では、Ackによってメッセージ配信がブロックされているわけではありませんが、メッセージを要求するPullリクエストが存在していません。サーバー再起動後にPull処理Threadやconsume()ループが正常に開始されたか、NATS再接続後にSubscriptionが正常に復旧したかを確認する必要があります。
3) Last DeliveredとAck Floorが長時間進行しない場合
Last DeliveredはConsumerがどこまでメッセージを配信したかを、Ack Floorは連続してどこまでAckが完了したかを示します。2つの値そのものの差よりも、時間が経過しても両方の値がまったく進行していないかを確認することが重要です。
Last Delivered Stream Sequence: 185400
Ack Floor Stream Sequence: 185380
OutStanding Acks: 20
Unprocessed Messages: 3270
数秒または数分が経過してもLast Deliveredが185400で停止し、Unprocessed Messagesが増え続けている場合は、Consumerの消費フローが滞っている状態を疑うことができます。このときはWaiting Pulls、Redelivered Messages、アプリケーションログなども併せて確認し、Consumer側の問題なのか、アプリケーションの処理上の問題なのかを切り分ける必要があります。
このような障害状況では、既存のDurable Consumerを削除した後、アプリケーションを再起動して新しいConsumerを作成すると、メッセージが正常に消費されました。これにより、PublisherやStream自体の問題というよりも、既存のDurable Consumerが異常終了前から保持していた状態が、メッセージ消費障害に影響していたことを確認できました。
その後、サーバーが再起動された場合に既存のConsumerを削除して再作成し、メッセージングシステムの状態を初期化する復旧スクリプトを構成しました。その結果、船舶におけるNATS JetStream関連の障害発生を減らすことができました。
4. おわりに
実際の運用環境では、サーバーの強制終了やアプリケーションの異常終了など、予期しない状況によってNATS JetStreamのメッセージ処理に障害が発生する可能性があります。特にメッセージ処理はサービス間のデータ連携に関わる重要な機能であるため、障害発生時に原因を迅速に把握し、適切な措置を講じることが重要です。
したがって、今後同様の障害が発生した場合は、本稿で整理したConsumerの主な状態値とケースごとの分析方法に基づいて障害原因を迅速に特定して対応し、サービス停止時間を最小限に抑え、安定した運用を維持できるようにする必要があります。
deeeneee