モノのインターネット (IoT) デバイスでは、インターネットに接続することが要件となります。IoT デバイスはインターネットに接続することで、他のデバイスやバックエンド・サービスと通信するからです。インターネットの基礎となっているネットワーク・プロトコルは TCP/IP であり、IoT 通信では TCP/IP スタックをベースに作成された MQTT (Message Queue Telemetry Transport) が標準的な通信プロトコルとなっています。
MQTT は、1990 年代後半に IBM が考案して開発したプロトコルです。当初は油田パイプラインに取り付けられたセンサーを衛星とリンクするために使用されていました。その名前が示唆するように、MQTT は 2 者間の非同期通信をサポートするプロトコルです。非同期メッセージング・プロトコルは、メッセージの送信側と受信側を空間という点でも、時間という点でも切り離すため、信頼できないネットワーク環境内でスケーラブルな性質を発揮します。一方、その名前に反して、MQTT はメッセージング・キューとはまったく関係がなく、パブリッシュ/サブスクライブ・モデルに従っています。2014 の後半になって MQTT は正式に OASIS オープン・スタンダードになりました。現在は、よく使われているプログラミング言語で、さまざまなオープンソース実装を使用して MQTT がサポートされるようになっています。
なぜ MQTT なのか
IoT 開発者にとって、MQTT は軽量さと柔軟性の点で申し分のないバランスがとれたネットワーク・プロトコルです。
- 軽量なプロトコルであるということは、制約が厳しいデバイス・ハードウェア上でも、待ち時間が長かったり帯域幅が限られていたりするネットワーク上でも実装できることを意味します。
- MQTT の柔軟性は、IoT デバイスおよびサービスの多種多様なアプリケーション・シナリオをサポートすることを可能にします。
IoT 開発者にとって、MQTT がいかに最適な選択肢であるかを理解するために、まずは、よく使われている他のネットワーク・プロトコルがなぜ IoT に向かないのかを考えましょう。
他のネットワーク・プロトコルが IoT に向かない理由
ほとんどの開発者はすでに HTTP Web サービスを十分に理解しているので、IoT デバイスを Web サービスに接続すればよいではないかと思うでしょう。Web サービスに接続すれば、デバイスのデータを HTTP リクエストとして送信し、システムからの更新を HTTP レスポンスとして受信できます。けれども、このリクエスト・レスポンス・パターンには重大な制約事項があります。
- HTTP は同期プロトコルです。したがって、クライアントはサーバーからのレスポンスを待機します。Web ブラウザーはこの要件に対応できますが、そのためにスケーラビリティーが犠牲になります。IoT の世界では、デバイスの数が多く、通常はネットワークに信頼性がないか、待ち時間が長いことから、同期通信は問題になります。非同期メッセージング・プロトコルのほうが IoT アプリケーションには断然適しています。非同期通信であれば、センサーが測定値を送信して、そのデータを宛先のデバイスやサービスに配信するのに最適なパスとタイミングの判断は、ネットワークに任せることができるためです。
- HTTP は片方向です。クライアントが接続を開始しなければなりません。IoT アプリケーションで通常クライアントとなるのはデバイスやセンサーです。つまり、デバイスまたはセンサーがネットワークから受動的にコマンドを受信することはできません。
- HTTP は 1 対 1 のプロトコルです。クライアントがリクエストを送信し、サーバーがそれに応答します。したがって、ネットワーク上のすべてのデバイスにメッセージをブロードキャストするのは困難であり、コストもかかります。けれども、IoT アプリケーションでは、このようにメッセージをブロードキャストすることが一般的な使用ケースとなっています。
- HTTP は多数のヘッダーとルールを伴う重量級のプロトコルです。帯域幅が限られたネットワークには適していません。
以上の理由から、ハイパフォーマンスでスケーラブルなシステムのほとんどでは、システム内のデータ交換に Web サービスではなく、非同期メッセージング・バスを採用しています。実際、エンタープライズ・ミドルウェア・システムでは、AMQP (Advanced Message Queuing Protocol) というメッセージング・プロトコルが最もよく使われています。けれどもこのようなハイパフォーマンス環境では、一般にコンピューティング能力とネットワーク待ち時間が問題になることはありません。AMQP は、エンタープライズ・アプリケーションに信頼性と相互運用性をもたらすために設計されていて、充実した機能一式を備えていますが、リソースに限りがある IoT アプリケーションには向いていません。
AMQP の他にも、よく使われているメッセージング・プロトコルがあります。その一例は、XMPP (Extensible Messaging and Presence Protocol) です。XMPP はピアツーピア・インスタント・メッセージング (IM) プロトコルであり、IM の使用ケースをサポートする機能 (プレゼンス、メディア添付など) に重点が置かれています。MQTT と比べると、XMPP はデバイス上でもネットワーク上でも遥かに多くのリソースを必要とします。
MQTT をこれほどまでに軽量かつ柔軟なプロトコルにしているのはなぜでしょうか? MQTT プロトコルの重要な特徴は、そのパブリッシュ・サブスクライブ・モデルです。あらゆるメッセージング・プロトコルの例に漏れず、MQTT はデータのパブリッシュ側と取り込み側を切り離します。
パブリッシュ・サブスクライブ・モデル
MQTT プロトコルではネットワーク内のエンティティーのタイプとして、メッセージ・ブローカーとクライアントの 2 つを定義しています。1 つのメッセージ・ブローカーに対し、多数のクライアントがあるという形です。ブローカーはサーバーとしてクライアントからのすべてのメッセージを受信し、それらのメッセージを該当する宛先クライアントにルーティングします。クライアントについては、ブローカーとやりとりしてメッセージを送受信できるものであれば、何でもクライアントに相当します。例えば、現場にある IoT センサーがクライアントであったり、IoT データを処理するデータ・センター内のアプリケーションがクライアントであったりします。
- クライアントがブローカーに接続します。クライアントは、ブローカー内の任意のメッセージ「トピック」にサブスクライブできます。この接続は、単純な TCP/IP 接続にすることも、機密性の高いメッセージ用の暗号化 TLS 接続にすることもできます。
- クライアントは特定のトピックに属するメッセージをパブリッシュするために、メッセージとトピックをブローカーに送信します。
- ブローカーは、そのトピックにサブスクライブしているすべてのクライアントにメッセージを転送します。
MQTT メッセージはトピック別に整理されるため、アプリケーション開発者は特定のクライアントが特定のメッセージだけを操作するように指定できるという柔軟性を利用できます。例えば、センサーがその測定値を「sensor_data」トピックの下でパブリッシュし、「config_change」をサブスクライブするとします。その場合、センサー・データをバックエンド・データベースに保存するデータ処理アプリケーションは、「sensor_data」トピックをサブスクライブすることになります。また、管理コンソール・アプリケーションがセンサーの構成 (感度やサンプル頻度など) を調整するためにシステム管理者から発行されたコマンドを受信すると、それらの構成変更を「config_change」トピックにパブリッシュするといった仕組みになります (図 1 を参照)。
図 1. IoT センサーの場合の MQTT パブリッシュ・サブスクライブ・モデル
パブリッシュ・サブスクライブ・モデルであると同時に、MQTT は軽量です。MQTT はメッセージ・タイプを指定する単純なヘッダー、テキスト・ベースのトピック、そして任意のバイナリー・ペイロードしか使いません。宛先クライアントがそのペイロードを解析できる限り、アプリケーションはペイロードに任意のデータ形式 (JSON、XML、暗号化バイナリー、Base64 など) を使用できます。
MQTT 開発を開始する
MQTT 開発を開始するのに最も簡単な方法は、MQTT SDK およびライブラリーを複数のプログラミング言語で提供する Eclipse Paho プロジェクト に含まれている、Python mosquitto モジュールというツールを使用することです。mosquitto モジュール内には、ローカル・コンピューター上で実行できる MQTT ブローカーと、メッセージを使ってこのブローカーとやりとりするためのコマンド・ライン・ツールがあります。mosquitto モジュールは、このリンク先の mosquitto Web サイト からダウンロードしてインストールできます。
MQTT ブローカーをローカル・コンピューター上で実行するには、mosquitto コマンドを使用します。このコマンドに -d オプションを指定して、MQTT ブローカーをバックグラウンドで実行することもできます。
$ mosquitto -d
続いて別のターミナル・ウィンドウ内で mosquitto_sub コマンドを実行することで、ローカル・ブローカーに接続して、トピックにサブスクライブできます。このコマンドを実行すると、コマンドは待機状態になって、サブスクリプションからメッセージを受信するたびに、そのメッセージを出力します。
$ mosquitto_sub -t "dw/demo"
さらに別のターミナル・ウィンドウ内で mosquitto_pub コマンドを実行すると、ローカル・ブローカーに接続して、トピックに対してメッセージをパブリッシュできます。
$ mosquitto_pub -t "dw/demo" -m "hello world!"
上記のコマンドによって、mosquitto_sub を実行しているターミナルの画面に、「hello world!」メッセージが出力されるはずです。これで、MQTT ブローカーを使用してメッセージを送受信したことになります!
もちろん、本番システム内ではローカル・コンピューターをブローカーとして使用することはできません。ブローカーとして使用するには、IBM Bluemix Internet of Things Platform サービスをお勧めします。このサービスは、MQTT ブローカーのように機能する、信頼性の高いオンデマンド・サービスです (このリンク先の サービスの資料 に、この Bluemix サービスがデバイスおよびアプリケーションとの通信プロトコルとして MQTT を統合し、使用する仕組みが詳しく説明されています)。
IBM Cloud Internet of Things Platform サービスは、以下のように機能します。
- Bluemix コンソールから、Internet of Things Platform サービスのインスタンスをオンデマンドで作成できます。
- 作成したサービス・インスタンスにデバイスを追加すると、そのデバイスが MQTT を使用してサービス・インスタンスに接続できるようになります。デバイスごとに ID と名前が割り当てられます。サービスにアクセスできるのは、リストアップされているデバイスだけです。それらのデバイスのトラフィックと使用状況についての情報は、Watson IoT Platform ダッシュボードに報告されます。
- Bluemix はデバイス・クライアントごとに、サービス・インスタンス (MQTT ブローカー) に接続する際に使用するホスト名、ユーザー名、パスワードを割り当てます (Bluemix 上では、ユーザー名は常に use-token-auth で、パスワードは接続された各デバイスに割り当てられたトークン (図 2 を参照) です)。
図 2. IBM Bluemix 内に作成された Internet of Things Platform サービス・インスタンス
リモート MQTT ブローカーを使用する場合、ブローカーのホスト名と認証資格情報を mosquitto_sub および mosquitto_pub コマンドに渡す必要があります。例えば、以下のコマンドは、Bluemix から提供されたユーザー名とパスワードを使用して、Internet of Things Platform サービス上の demo トピックにサブスクライブします。
$ mosquitto_sub -t "demo" -h host.iotp.mqtt.bluemix.com -u username -P password
mosquitto ツールの使用方法と、mosquitto API を使用して独自の MQTT クライアント・アプリケーションを作成する方法について詳しくは、このリンク先の mosquitto Web サイト上の資料 を参照してください。
必要なツールが揃ったので、MQTT プロトコルをさらに詳しく探りましょう。
MQTT プロトコルを理解する
MQTT は、データ・バイトの編成方法および TCP/IP ネットワークを介して送信する方法を指定するワイヤー・プロトコルです。けれども実際上は、開発者がこのワイヤー・プロトコルを理解する必要はありません。各メッセージがコマンドとデータ・ペイロードで構成されることを理解していれば十分です。メッセージのコマンドの部分は、メッセージのタイプ (例えば、CONNECT メッセージや SUBSCRIBE メッセージなど) を定義します。すべての MQTT ライブラリーおよびツールは例外なく、これらのメッセージを直接扱うための単純な方法を用意していて、メッセージやクラインド ID などの必須フィールドに自動的に値を取り込めるようになっています。
まず、クライアントがブローカーに接続するために、CONNECT メッセージを送信します。CONNECT メッセージは、クライアントからブローカーへの接続を確立するためのものです。CONNECT メッセージには、以下のコンテンツ・パラメーターがあります。
表 1. CONNECT メッセージのパラメーター
パラメーター | 説明 |
cleanSession | このフラグは、接続を持続させるかどうかを指定します。持続セッションでは、すべてのサブスクリプションと、場合によっては (QoS に依存) 送受信に失敗したメッセージをブローカー内に保存します (QoS の説明については、表 3 を参照)。 |
username | ブローカーの認証・許可資格情報 |
password | ブローカーの認証・許可資格情報 |
lastWillTopic | 接続が予期せずに中断された場合、ブローカーは自動的に「last will」メッセージをトピックにパブリッシュします。 |
lastWillQos | 「last will」メッセージの QoS (QoS の説明については、表 3 を参照)。 |
lastWillMessage | 「last will」メッセージ自体。 |
keepAlive | クライアントが接続を維持するためにブローカーに対して ping を実行する時間間隔を指定します。 |
クライアントはブローカーから CONNACK メッセージを受信します。CONNACK メッセージには、以下のコンテンツ・パラメーターがあります。
表 2. CONNACK メッセージのパラメーター
パラメーター | 説明 |
sessionPresent | この接続ですでに持続セッションが行われているかどうかを示します。つまり、この接続はトピックをサブスクライブ済みであり、送受信に失敗したメッセージが配信されます。 |
returnCode | 0 は成功を意味します。その他の値は、エラーの原因を識別します。 |
接続の確立後、クライアントは特定のトピックに関するメッセージをブローカーから受信することを示すために、1 つ以上の SUBSCRIBE メッセージをブローカーに送信できます。メッセージでは、以下のパラメーターが 1 回以上繰り返される場合があります。
表 3. SUBSCRIBE メッセージのパラメーター
パラメーター | 説明 |
qos | qos (サービス品質: QoS) フラグは、このトピックに属するメッセージを、どの程度の確実さでクライアントに配信するかを指定します。
|
topic | サブスクライブ対象のトピック。各トピックをスラッシュ文字で区切って、複数のレベルからなるトピックにすることもできます。例えば、「dw/demo」、「ibm/bluemix/mqtt」も有効なトピックです。 |
クライアントが正常にトピックをサブスクライブすると、ブローカーは 1 つ以上の returnCode パラメーターを設定した SUBACK メッセージを返します。
表 4. SUBACK メッセージのパラメーター
パラメーター | 説明 |
returnCode | SUBCRIBE コマンド内で指定されたトピックごとに 1 つの戻りコードが存在します。以下の戻りコードがあります。
|
SUBSCRIBE メッセージに対応するメッセージとして、クライアントは UNSUBSCRIBE メッセージを送信して 1 つ以上のトピックからサブスクリプションを解除することもできます。
表 5. UNSUBSCRIBE メッセージのパラメーター
パラメーター | 説明 |
topic | 複数のトピックがある場合、このパラメーターが繰り返されます。 |
クライアントは、PUBLISH メッセージをブローカーに送信できます。このメッセージには、トピックとデータ・ペイロードが含まれます。ブローカーは、そのトピックをサブスクライブしているすべてのクライアントにメッセージを転送します。
Table 6. 表 6.PUBLISH メッセージのパラメーター
パラメーター | 説明 |
topicName | パブリッシュされるメッセージが属するトピック。 |
qos | メッセージ配信のサービス品質レベル (QoS の説明については、表 3 を参照)。 |
retainFlag | このフラグは、ブローカーがメッセージをこのトピックで最後に既知となったメッセージとして保存するかどうかを指定します。 |
payload | メッセージに含まれる実際のデータ。テキスト文字列またはデータのバイナリー BLOB のいずれかです。 |
ヒントと次善策
MQTT の力は、そのシンプルさにあります。使用できるトピックやメッセージ・ペイロードの種類に関する制約は一切ありません。したがって、興味深い使用ケースに対応することができます。例えば、以下の質問を考えてください。
MQTT を使用して、メッセージを 1 対 1 で送受信するにはどうすればよいですか?メッセージの送信側と受信側が、固有のトピックを使用することに合意できます。例えば、トピック名に両方のクライアントの ID を含めれば、その固有性が確実になります。
クライアントがそのプレゼンス状況をブロードキャストするには、どうすればよいですか?その場合は、システムが「presence」トピックの命名規則について合意するという方法があります。例えば、「presence/client-id」トピックを使用してクライアントのプレゼンス情報を保持するようにします。クライアントはメッセージを、接続中は true に設定し、切断中は false に設定します。クライアントは last will メッセージを false に設定して、接続が中断された場合には false に設定されるようにすることもできます。メッセージはブローカーによって保存できるため、新しいクライアントはトピックを読み取って、プレゼンス状況を確認することができます。
通信をセキュアにするにはどうすればよいですか?クライアントとブローカー間の接続は、送信中のデータを保護するために暗号化 TLS 接続にすることができます。さらに、MQTT プロトコルではペイロードのデータ形式に一切の制約が課せられないため、システムが暗号化方式と鍵更新メカニズムについて合意するという方法もあります。その上で、ペイロードに含まれるすべての内容を、実際の JSON または XML メッセージの暗号化バイナリー・データにします。
まとめ
この記事では、MQTT プロトコルを技術の面から紹介し、MQTT とは何か、MQTT が IoT アプリケーションに適している理由、そして MQTT を使用したアプリの開発を始める方法を説明しました。