ネットワーク●●●○○

MQTT

えむきゅーてぃーてぃー

非力な機器と不安定な回線のために作られた軽量pub/sub型メッセージングプロトコル。

概要

MQTT(MQ Telemetry Transport)は、センサーや家電のような非力な機器が、細い・不安定な回線ごしにメッセージをやり取りするために設計された、軽量な pub/sub(出版/購読)型のメッセージングプロトコルです。固定ヘッダはわずか 2 バイトからで、電池駆動のマイコンでも実装・維持できる小ささが持ち味です。

登場するのは IoT(モノのインターネット)の現場が中心です。温度センサーが測定値を送る、スマートロックに施錠指示を届ける、工場の設備が稼働状況を報告する — こうした「小さなメッセージを大量の機器と細く長くやり取りする」場面で、MQTT は事実上の標準になっています。AWS IoT Core や Azure IoT Hub といったクラウドの IoT 基盤も、機器との会話には MQTT を採用しています。

なぜ生まれたか

MQTT の原型は 1999 年、石油パイプラインの監視のために IBM のエンジニアらが設計しました。砂漠に点在するセンサーは衛星回線で通信しており、回線は細く、従量課金で、頻繁に切れる。そんな環境で HTTP のようなプロトコルを使うと、リクエストのたびに長いヘッダが付き、接続の確立と切断を繰り返し、貴重な帯域と電力を食い潰してしまいます。また HTTP は「クライアントが尋ねてサーバが答える」形なので、サーバ側から機器へ指示を押し届けるのも不得手でした。

そこで、(1) ヘッダを極限まで削る、(2) TCP 接続を張りっぱなしにして双方向にメッセージを押し流す、(3) 送り手と受け手を直接つながず中間のブローカーで仲介する、という設計が選ばれました。特に (3) が重要で、いつ切れるかわからない何万台もの機器同士が互いの生死やアドレスを気にし合うのは不可能です。全員が 1 か所のブローカーとだけ会話すればよい、という pub/sub 構造が、不安定な大量接続の問題を一挙に解きました。

詳細

ブローカーを中心とした pub/sub 構造

MQTT の登場人物は「ブローカー」と「クライアント」の 2 種類だけです。ブローカーはメッセージの集配所となるサーバ(Mosquitto や EMQX が代表的な実装)で、すべてのクライアントはブローカーに接続します。メッセージを送る側(publisher)は「トピック」という宛名を付けて publish し、受け取る側(subscriber)は関心のあるトピックを事前に subscribe しておくと、該当メッセージがブローカーから配送されてきます。送り手は「誰が受け取るか」を知らず、受け手は「誰が送ったか」を知らなくてよい — この疎結合が pub/sub の核心です。

温度センサーpublish home/temp湿度センサーpublish home/humidスマートロックsubscribe home/lockブローカートピックごとに振り分け監視ダッシュボードsubscribe home/#制御アプリpublish home/lock全クライアントはブローカーとだけ接続を張り、互いのアドレスや生死を知らなくてよい
ブローカーを介した pub/sub — 送り手と受け手は互いを知らず、トピックだけで結ばれる

トピック階層とワイルドカード

トピックは home/livingroom/temperature のようにスラッシュ区切りの階層で表現します。購読側はワイルドカードが使え、+ は 1 階層任意(home/+/temperature で全部屋の温度)、# は以降すべて(home/# で家中の全メッセージ)にマッチします。トピックは事前登録不要で、publish した瞬間に存在し始めます。この身軽さは強力ですが、裏を返せば命名規約を決めないとトピック空間がすぐ無法地帯になるため、実務では「場所/機器種別/計測項目」のような階層設計を最初に固めるのが定石です。

QoS — 3 段階の配達保証

不安定な回線を前提とする MQTT は、メッセージごとに配達保証レベル(QoS)を選べます。QoS 0 は「最大 1 回」— 投げっぱなしで、届かなくても再送しません。毎秒流れるセンサー値のように「次の値がすぐ来るから 1 個落ちても困らない」データ向きです。QoS 1 は「最低 1 回」— 受領確認が返るまで再送するため必ず届きますが、確認が失われた場合に重複して届く可能性があり、受信側には冪等性(同じメッセージを 2 回処理しても結果が変わらない性質)が求められます。QoS 2 は「正確に 1 回」— 4 往復のハンドシェイクで重複も欠落も防ぎますが、その分遅く重くなります。課金イベントのような「二重処理が事故になる」メッセージに限定して使うのが普通です。

遺言と保持 — 不安定さを前提にした道具立て

MQTT には、切断が日常であることを前提にした気の利いた機能が 2 つあります。ひとつが Last Will(遺言)です。クライアントは接続時に「自分が突然いなくなったら、このトピックにこのメッセージを流してほしい」とブローカーに預けておけます。電池切れや圏外で機器が黙って消えても、ブローカーが代わりに「offline」を配送してくれるため、購読側は機器の生死を把握できます。もうひとつが Retained Message(保持メッセージ)で、トピックごとに最後の 1 通をブローカーが記憶し、新しく購読を始めたクライアントへ即座に渡します。これがないと、1 時間に 1 回しか値を送らないセンサーの現在値を知るために最大 1 時間待つことになります。

メッセージキューとの違い、WebSocket との併用

pub/sub と聞くとメッセージキュー(RabbitMQ や Kafka)を思い浮かべるかもしれません。送り手と受け手を仲介者で疎結合にする発想は共通ですが、力点が違います。メッセージキューはサーバ間連携のための基盤で、メッセージの蓄積・再読・分散処理に強く、その分リッチで重い。MQTT は「非力な端末との細い接続を大量に維持する」ことに特化し、蓄積機能は最小限(保持メッセージと、切断中の購読者向けの一時保存程度)です。実務では「機器からブローカーまでは MQTT、ブローカーから先の業務処理は Kafka などへ流し込む」という組み合わせが定番です。

また、ブラウザは生の TCP ソケットを開けないため、Web ページから MQTT を話すときは WebSocket の上に MQTT を載せるトンネリングを使います。主要ブローカーはこの口を標準で備えており、ダッシュボードのブラウザ画面がセンサー値をリアルタイム受信する、といった構成が手軽に作れます。なお本番運用では TLS での暗号化とクライアント認証がほぼ必須です。ポート 1883(平文)を公開したまま放置されたブローカーが世界中から覗かれる、というのは IoT セキュリティの定番の事故例です。