

MQTTのCorrelation Data、v5はプロパティ、v3.1.1はペイロードに自分で埋めるしかない
この記事は、構成と文章の整理にAIを使っています。
あくまで読みやすくするための補助で、内容そのものをAIにすべて任せて書いているわけではありませんが、ところどころ相談しながら整えています。
MQTTでリクエスト/レスポンス的なやり取りをしたいとき、返ってきたメッセージがどのリクエストに対応するのか、という「相関」の話。v5からはCorrelation Dataというプロパティが追加されていて、v3.1.1にはこれがない。じゃあv3.1.1だとどうするか、というのを整理する。
そもそも何のためにあるのか
MQTTはpub/subが基本で、本来「誰が誰に何を返す」という概念がない。1つのレスポンストピックに複数のリクエスタからの応答が流れ込んでくることもあるし、1つのリクエスタが複数のリクエストを同時に投げることもある。そうなると、届いたレスポンスが「自分が投げたどのリクエストに対する返事なのか」を判断する手段が必要になる。
Correlation Dataはまさにそのためのもの。リクエスト送信時に一意な値を持たせておいて、レスポンダはそれをそのまま返す。リクエスタは受信したレスポンスのCorrelation Dataを見て、手元で管理している「送信済みリクエストの一覧」と突き合わせれば、どのリクエストへの応答かが判別できる。中身の意味づけ(UUIDにするか連番にするか)はアプリ側の自由で、MQTTのプロトコル自体は「そのまま送り返す箱」を用意しているだけ。
v5: Correlation DataとResponse Topicはプロトコルのプロパティ
MQTT v5のPUBLISHパケットには、Properties(可変ヘッダー)として以下が正式に追加されている。
Response Topic: レスポンスを送ってほしいトピックCorrelation Data: 任意長のバイナリ。中身の意味はMQTT側では規定されていない
流れはこう。
sequenceDiagram
participant Requester
participant Responder
Requester->>Responder: PUBLISH things/request<br/>Properties: ResponseTopic=res/123, CorrelationData=0xAB1
Note over Responder: 受け取ったCorrelationDataを<br/>そのままコピーする
Responder->>Requester: PUBLISH res/123<br/>Properties: CorrelationData=0xAB1<br/>Payload: 元のまま
ポイントは、相関情報がプロトコルのメタデータ側に乗っていて、ペイロード(JSONなど)には一切手を加えなくていいところ。レスポンダは受け取ったCorrelation Dataを無加工でコピーして返すだけでいい。中身がUUIDでも連番でもバイナリIDでも、MQTT自体は関知しない。
クライアントライブラリ側もオプションとして普通に用意されていて、例えばMQTT.jsならpublishのオプションにproperties: { correlationData, responseTopic }を渡すだけで済む。ペイロードのシリアライズには一切絡んでこない。
v3.1.1: プロパティが存在しないのでペイロードに埋めるしかない
v3.1.1にはPropertiesという概念自体がない。なので相関を取りたければ、アプリ層でペイロードに識別子を持たせるしかない。典型的にはJSONのボディにcorrelationIdのようなフィールドを追加する。
sequenceDiagram
participant Requester
participant Responder
Requester->>Responder: PUBLISH things/request<br/>Payload: { correlationId: "abc123", ... }
Note over Responder: correlationIdを読み取り<br/>レスポンスにも同じ値を入れる
Responder->>Requester: PUBLISH things/response<br/>Payload: { correlationId: "abc123", result: ... }
ブローカーはペイロードの中身を一切解釈しないので、これは完全にアプリケーション側の実装依存。リクエスタ・レスポンダ双方で「JSONのこのフィールドが相関ID」という契約を決め打ちしておく必要がある。
やっていることの目的自体はv5と同じで、「このレスポンスがどのリクエストに対する返事か」をリクエスタ側で判別するため。ただし判別のための仕組みをプロトコルが提供してくれないので、識別子の発行・埋め込み・突き合わせを全部アプリ側で組む必要がある、という違い。
実装で気をつけること
- v3.1.1側: リクエスト送信時にUUID等を発行して
correlationIdに入れる。リクエスタは自分が発行したIDと受信メッセージを突き合わせる必要があるので、pending map(発行したID→Promiseやコールバック)を自前で持つことになる - v5側: ライブラリのオプションに渡すだけで、ペイロードのスキーマを汚さない。バイナリで持てるので、文字列のUUIDより多少軽い
- ブローカーやライブラリの制約でv5に上げられない環境なら、ペイロード埋め込み一択。選択の余地はない
v5に上げられるならプロパティに任せた方が素直だし、ペイロードのスキーマも綺麗に保てる。逆にv3.1.1縛りの環境では、correlationIdをどこに置くかだけ決めてしまえば普通に動く。