メッセージ再送信
メッセージ再送信はMQTTプロトコル仕様の一部です。
プロトコルでは、通信当事者であるサーバーおよびクライアントが送信するPUBLISHパケットは、以下のようなQoS(サービス品質)レベルの要件を満たす必要があると規定されています。
- QoS 1: メッセージが少なくとも1回は配信されることを意味します。つまり、送信者は相手からの確認応答を受け取るまで、常にメッセージを再送信します。これにより、同じQoS 1メッセージがMQTTプロトコルの上位層(サービスのアプリケーション層)で複数回受信される可能性があります。
- QoS 2: メッセージが正確に1回だけ配信されることを意味します。つまり、上位層ではメッセージが1回だけ受信されます。
QoS 1およびQoS 2のPUBLISHパケットはMQTTプロトコルスタック層で再送信されますが、以下の点に注意してください。
- QoS 1メッセージの再送信が発生した場合、再送信されたPUBLISHパケットもMQTTプロトコルスタックの上位層で受信されます。
- QoS 2メッセージは再送信されても、MQTTプロトコルスタックの上位層では1回のPUBLISHパケットしか受信されません。
基本設定
EMQXは以下のシナリオで未確認のQoS 1およびQoS 2メッセージを再送信します。
- クライアントが再接続してセッションを再開するとき、EMQXは未確認メッセージを再生してQoS配信フローを継続します。
- クライアントが接続中に、
mqtt.retry_intervalが正の期間に設定されている場合、EMQXは定期的に未確認メッセージを再送信します。
設定ファイルで再送信間隔を設定します。
| 設定項目 | 型 | オプション値 | デフォルト値 | 説明 |
|---|---|---|---|---|
retry_interval | duration | duration または infinity | infinity | クライアント接続中に未確認のQoS 1およびQoS 2メッセージを再送信する間隔。0またはinfinityに設定すると定期再送信を無効化。セッション再開時には未確認メッセージは再生されます。 |
一般的には上記内容のみを把握していれば十分です。
EMQXがMQTTプロトコルの再送信をどのように処理するかの詳細は、本記事の以下を参照してください。
プロトコル仕様と設計
再送信対象
まず、EMQXの再送信機構設計を理解する前に、プロトコルにおけるQoS 1およびQoS 2の送信プロセスを理解している必要があります。未確認の場合はMQTTv3.1.1 - QoS 1: 少なくとも1回の配信およびMQTTv3.1.1 - QoS 2: 正確に1回の配信を参照してください。
ここでは簡単に復習し、異なるQoSにおける再送信対象を示します。
QoS 1
QoS 1はメッセージが少なくとも1回配信されることを要求するため、送信者が確認応答を受け取るまでMQTTプロトコル層でメッセージが継続的に再送信される可能性があります。
プロセスの概略図は以下の通りです。
PUBLISH
#1 Sender ---------------> Receiver (*)
PUBACK
#2 Sender <--------------- Receiver- 2つのパケットが関与し、送信者と受信者でそれぞれ1回ずつ送信動作があります。両パケットは同じPacketIdを保持します。
- 行末に
*がある場合、送信者は確認応答待ちがタイムアウトした場合に再送信を開始する可能性があります。
このことから、QoS 1メッセージはPUBLISHメッセージのみ再送信されます。
QoS 2
QoS 2はメッセージが正確に1回だけ配信されることを要求するため、より複雑なプロセスが必要です。概略図は以下の通りです。
PUBLISH
#1 Sender ---------------> Receiver (*)
PUBREC
#2 Sender <--------------- Receiver
PUBREL
#3 Sender ---------------> Receiver (*)
PUBCOMP
#4 Sender <--------------- Receiver- 4つのパケットが関与し、送信者と受信者それぞれが2回ずつ送信動作を行います。これら4つのパケットはすべて同じPacketIdを保持します。
- 行末に
*がある場合、送信者は確認応答待ちがタイムアウトした場合に再送信を開始する可能性があります。
これより、QoS 2メッセージはPUBLISHパケットとPUBRELパケットのみ再送信されます。
まとめると、
- 再送信動作は、メッセージ送信後に指定時間内に期待される応答が受信できなかった場合にトリガーされます。
- 再送信対象は以下の3種類に限定されます。
- QoS 1のPUBLISHパケット
- QoS 2のPUBLISHパケット
- QoS 2のPUBRELパケット
EMQXがPUBLISHメッセージの受信者として動作する場合は、再送信操作は不要です。
インフライトウィンドウと最大受信値
この概念の定義と説明はInflight Window and Message Queueを参照してください。
これらの概念を導入する目的は以下の理解のためです。
- EMQXが送信者として動作する場合、再送信されるメッセージはインフライトウィンドウに格納されているメッセージでなければなりません。
- EMQXが受信者として動作し、送信者がメッセージを再送信した場合:
- QoS 1では、EMQXは直接PUBACKで応答します。
- QoS 2では、EMQXは最大受信メッセージキューに格納されているPUBLISHまたはPUBRELパケットを解放します。
メッセージの順序
上記の概念は理解しておけば十分です。最も注意すべきは、メッセージが再送信された後のメッセージ順序の変化、特にQoS 1メッセージの場合です。例として:
現在のインフライトウィンドウのサイズが2に設定されている状況で、EMQXがクライアントのあるトピックに4つのQoS 1メッセージを配信しようとします。途中でクライアントプログラムまたはネットワークに問題が発生した場合、送信プロセスは以下のようになります。
#1 [4,3,2,1 || ] -----> []
#2 [4,3 || 2, 1] -----> [1, 2]
#3 [4 || 3, 2] -----> [1, 2, 3]
#4 [4 || 3, 2] -----> [1, 2, 3, 2, 3]
#5 [ || 4] -----> [1, 2, 3, 2, 3, 4]
#6 [ || ] -----> [1, 2, 3, 2, 3, 4]このプロセスは6ステップあり、左側はEMQXのメッセージキューとインフライトウィンドウ(||で区切り)、右側はクライアントが受信したメッセンスの順序を示します。各ステップの説明は以下の通りです。
- ブローカーは4つのメッセージをメッセージキューに格納します。
- ブローカーは順に
1、2を送信し、インフライトウィンドウに格納します。クライアントはメッセージ1にのみ応答し、この時点でクライアントの送信ストリームに問題が発生し、以降の応答は送信されません。 - ブローカーはメッセージ
1の応答を受信し、インフライトウィンドウから1を削除し、3を送信します。2と3の応答を待ち続けます。 - 応答待ちがタイムアウトすると、ブローカーはメッセージ
2、3を再送信します。クライアントは再送信されたメッセージ2、3を受信し、正常に応答します。 - ブローカーはインフライトウィンドウから
2、3を削除し、メッセージ4を送信します。クライアントはメッセージ4を受信し、応答します。 - すべてのメッセージ処理が完了します。クライアントが受信したメッセージの順序は
[1, 2, 3, 2, 3, 4]であり、MQTTプロトコルスタックの上位層に順番に報告されます。
重複したメッセージが存在しますが、これはプロトコル仕様に完全に準拠しています。各メッセージの初回出現は順序通りであり、繰り返し受信されるメッセージ2、3には再送信メッセージであることを示す識別ビットが付与されます。
MQTTプロトコルおよびEMQXはこのトピックをOrdered Topicとして扱います。詳細はMQTTv3.1.1 - メッセージの順序付けを参照してください。
これにより、同一トピックおよびQoSにおいてメッセージが順序通りに配信および応答されることが保証されます。
なお、すべてのトピックにおけるQoS 1およびQoS 2メッセージを厳密に順序付けしたい場合は、インフライトウィンドウの最大長を1に設定する必要がありますが、クライアントのスループットは低下します。
関連設定
以下は上記機構で使用される設定項目の一覧です。すべて設定ファイルに含まれています。
| 設定項目 | 型 | オプション値 | デフォルト値 | 説明 |
|---|---|---|---|---|
mqueue_store_qos0 | boolean | true, false | true | QoS 0メッセージをメッセージキューに保存するかどうか。 |
max_mqueue_len | integer | 0 - infinity | 1000 | メッセージキューの最大長。 |
max_inflight | integer | 1 - 65535 | 32 | EMQXが同時に配信可能な未確認のQoS 1およびQoS 2メッセージの最大数。 |
max_awaiting_rel | integer | 0 - infinity | 100 | 各セッションでPUBRELを待機中のQoS 2メッセージの最大数。 |
await_rel_timeout | duration | > 0 | 300s | PUBRELの待機最大時間。タイムアウト時にEMQXはパケットIDを解放し、警告ログを出力します。QoS 2メッセージはEMQXがPUBREL待機を開始する前に配信されます。 |