メッセージ再送信
メッセージ再送信はMQTTプロトコル仕様の一部です。
このプロトコルでは、通信当事者であるサーバーとクライアントが送信するPUBLISHパケットは、以下のようなQoS(サービス品質)レベルの要件を満たす必要があります。
- QoS 1: メッセージが少なくとも1回は配信されることを意味します。つまり、送信者はピアからの確認を受け取るまで常にメッセージを再送信します。このため、同じQoS 1メッセージがMQTTプロトコルの上位層(サービスのアプリケーション層)で複数回受信される可能性があります。
- QoS 2: メッセージがちょうど1回だけ配信されることを意味します。つまり、上位層でメッセージは一度だけ受信されます。
QoS 1およびQoS 2のPUBLISHパケットはMQTTプロトコルスタック層で再送信されますが、以下の点に注意してください。
- QoS 1メッセージの再送信が発生した場合、再送信されたPUBLISHパケットもMQTTプロトコルスタックの上位層で受信されます。
- QoS 2メッセージはどのように再送信されても、MQTTプロトコルスタックの上位層では1つのPUBLISHパケットのみが受信されます。
基本設定
メッセージが再送信されるシナリオは以下の2つです。
- PUBLISHパケットをピアに送信後、指定時間内に応答がない場合、パケットを再送信する。
- セッションを維持しつつクライアントが再接続した後、EMQXは未応答のメッセージを自動的に再送信し、正しいQoS処理を保証する。
設定ファイルで以下のように設定可能です。
| 設定項目 | 型 | オプション値 | デフォルト値 | 説明 |
|---|---|---|---|---|
| retry_interval | duration | - | 30s | タイムアウト間隔を待ち、応答がなければメッセージを再送信する時間間隔 |
一般的には上記内容のみを意識すれば十分です。
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 送信者 ---------------> 受信者 (*)
PUBACK
#2 送信者 <--------------- 受信者- 2つのパケットが関与し、送信者と受信者それぞれ1回ずつ送信動作があります。両パケットは同じPacketIdを保持します。
- 行末に*がある場合、確認メッセージの待機がタイムアウトした際に送信者が再送信を開始する可能性を示します。
つまり、QoS 1メッセージはPUBLISHメッセージのみ再送信されます。
QoS 2
QoS 2はメッセージがちょうど1回配信されることを要求するため、より複雑なプロセスが必要です。概略図は以下の通りです。
PUBLISH
#1 送信者 ---------------> 受信者 (*)
PUBREC
#2 送信者 <--------------- 受信者
PUBREL
#3 送信者 ---------------> 受信者 (*)
PUBCOMP
#4 送信者 <--------------- 受信者- 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 | bool | true, false | true | QoS 0メッセージをメッセージキューに保存するかどうか |
| max_mqueue_len | integer | >= 0 | 1000 | メッセージキューの長さ |
| max_inflight | integer | >= 0 | 0 | インフライトウィンドウのサイズ。デフォルト0は無制限を意味する |
| max_awaiting_rel | integer | >= 0 | 0 | 最大受信数。デフォルト0は無制限を意味する |
| await_rel_timeout | duration | > 0 | 300s | 最大受信での解放待機の最大タイムアウト値。超過するとメッセージは破棄される |