sgur
5/18/2015 - 5:15 AM

MQTT とはなんだったのか

MQTT とはなんだったのか

#######################
MQTT とはなんだったのか
#######################

:更新: 2017-05-09
:作者: @voluntas
:バージョン: 3.14
:URL: http://voluntas.github.io/

**MQTT をググって調べた人向け**

**これはバージョン 3.1.1 を前提に書いてる**

注意
====

**今後は間違いの修正以外は更新を行いません**

既に自分は MQTT を追いかけていないため、この記事は古くなっています。この記事は古い MQTT の記事と認識して読んで頂ければと思います。

著者
====

クラスタリング可能な MQTT ブローカーの設計と開発、MQTT ゲートウェイの設計経験あり。

http://akane.shiguredo.jp や http://fuji.shiguredo.jp の設計者。

- `時雨堂 MQTT as a Service Sango 開発ログ <https://gist.github.com/voluntas/b5ae2c8e554dc1a70f0b>`_
- `時雨堂 MQTT ブローカー Akane 開発ログ <https://gist.github.com/voluntas/558fea1445253e6dc6c2>`_
- `時雨堂 MQTT ゲートウェイ Fuji 開発ログ <https://gist.github.com/voluntas/23132cd3848af5b3ee1e>`_

間違いについて
--------------

@voluntas まで Twitter にてリプライを飛ばして欲しい。

目的
====

MQTT が流行ってるのかどうかわからないが、ググってとりあえずまとめた系の記事が増えてきた。
ということで **MQTT 記事を公開するときに、間違いに気づくため** に書いてる。

間違い
======

MQ なので順序保証される
-----------------------

以下をとりあえず読むこと。

`MQTTのMQはMessage Queueではありません <http://tdoc.info/blog/2015/02/09/mqtt_is_not_queue.html>`_

ちなみに、MQTT ではまったく順序保証がどうこうって仕様でまったくない、ただ実装として頑張ることはできるが普通はやらない。

配信できるメッセージがそんなに大きくない
----------------------------------------

256 メガバイトが最大。これ結構知らない人が多い。

接続が切れた時メッセージを発行してくれる Will
---------------------------------------------

**意図しない** 場合のみ。MQTT ではクライアントが DISCONNECT を送って接続を終了する、その場合はメッセージは発行できない。

大量配信に向いてる
------------------

完全に実装とマシンリソースに依存する。MQTT **だから** ではない。

AMQP や JMS も PubSub が出来る。

電力消費が HTTP に比べて軽量
----------------------------

処理数を考えるならそうではあるけど、TLS を実際に使うだろうし、状況によるので言い切るのは危険。

実際に検証した人もいたが、正直なんともいえないという状況。

ヘッダーが最小 2 バイトなので HTTP に比べて軽量
-----------------------------------------------

たしかに最小なのだが、Payload が 1 メガバイト とかになったら、完全に誤差になる

あくまで、送る頻度が多く、Payload が小さいときのみ HTTP と比べて軽量

セキュリティ機能がない
----------------------

MQTT over TLS が使える。 TLS 1.2 で ECHDE-RSA で AES-GCM でも使えば少しは軽量になる。

TLS が重い
----------

まずはこれを読むこと。

`細かすぎて伝わらないSSL/TLS - Yahoo! JAPAN Tech Blog <http://techblog.yahoo.co.jp/infrastructure/ssl-session-resumption/>`_

1 回目はしょうが無いとしても、再接続時に TLS 1.2 で ECDHE-RSA で AES-GCM で SessionTickets や RenegotiationInfo で resumption を有効にしておけば、色々省略できる。

それでも重いというなら重いです。

実装がシンプル
--------------

クライアントの実装はたしかに、プロトコル部分だけであればそこそこシンプル。
ただし、実際に使う場合は非同期で実装できる必要があったりと色々ある。

基本的には自前実装ではなく既存のライブラリを使うのが良い。

**ただし** 、簡単に作れるため、いけてないライブラリもおおい。そこは調査するのが良い。

QoS 保証
--------

MQTT の QoS はクライアントとブローカーの間の保証であって、クライアントとブローカーとクライアントの保証ではない。

この誤解はとても多い。さらにパブリッシャーが QoS 2 で投げたとしても、サブスクライバー側が QoS 0 の場合は QoS 0 として届けられる。これを QoS ダウングレードと呼ぶ。

気軽にスケールさせられる
------------------------

そんなことはない。MQTT は、データベースのようにデータを保持する機能もある。
そのため複数台にした場合、これらのデータを共有する必要がある。

大量にデータが送られてくる場合はデータの部分が問題になる。これは落としどころを探して解決するしかない。

例えば IBM の MQTT ブローカー製品は 2 台構成で、メモリを直結したりとハードで解決している。
時雨堂の Akane では、クラスター構成をとれるが、Session や Retain のデータまでは同期しない。

バックエンドを分散 KVS にするという戦略もある。選択肢は色々。

MQTT Brokerはステートフルなため気軽にスケールしない。

接続が切れるとその間に送られてきたデータを受け取れない
------------------------------------------------------

これはセッションという機能を有効にすることで、接続が切れた状態で送られてきたデータを受け取ることが可能である。

機能としてはわかりづらいが CleanSession を false にした状態で接続すれば、切断したとしてもその間に受け取ったデータを MQTT Broker が保持をしてくれ、再接続した場合に受け取ることができる。

MQTT ブローカーからの PUB を受けとれるようにするために接続しっぱなしにしておくべきだ
------------------------------------------------------------------------------------

セッション機能を使って定期的にメッセージが来ていないかを確認することで、基本的には切断しておくという手法も取れるので、言い切るべきではない。

これは要件による。接続しっぱなしにするというのはほぼリアルタイムに別クライアントからのメッセージを受け取れるということで、それが要件にあるかどうかが判断のポイントだろう。

正しい
=======

暗号化の通信コストが高い
------------------------

正しい。MQTT の暗号化は TLS を使用することがほとんどだ。独自で実装するくらいならそもそも MQTT っぽいのを 1 から実装する方が賢明だろう。

TLS の暗号化は 1.0 と 1.2 (1.1 はマニアックなので省略する) で異なるのでちょっと解説をいれて説明していく。

そのまえに、MQTT は TCP を張りっぱなしで使うのが主な目的なので、初回接続の ClientHello ~ Finish までが重いとかコストが高いとかは当たり前の話なので省略する。一回張ってしまえば終わりだしあとは resumption で省略される。

さて TLS 1.0 の場合、高い可能性で AES-128-CBC で MAC 用のハッシュ関数が SHA が採用されることが多い。
となると 1 バイトのパケットを暗号化した場合はハッシュ値、さらにパディング、そしてパディングの長さそれぞれが足されていく。

パディングの説明はもう少し細かくできるが、ココでは省略。

暗号化をするためにデータが 16 の倍数である必要がある。

となると 1 + 20 + 1 に対して + 10 の padding 、おそらく 1 バイトから 32 バイトまで跳ね上がることに。

TLS 1.2 の場合は AES-128-GCM の AEAD 暗号が選択されることが多いです。MAC ハッシュは 256 か 384 ですが 256 のことが多い。

となると 1 バイトのデータに対して、32 バイトのハッシュ、さらには TLS 1.2 の場合は先頭に IV を仕込むので、ここでは 4 バイト。36 バイトデータが増えることになる。AEAD の場合は padding はないので padding は省略する。

1 バイトのパケットを送ったら 36 バイトまで跳ね上がる。

つまり、暗号化を行うと通信コストが高くなる。

ただし、暗号化はその通信コストを払うことで安全を保っている。特に TLS という検証されてきた暗号方式を使うことはとても重要だ。

サーバーまでの暗号方式に独自の暗号方式を採用するのは、できるだけ避けるべきだ。
なぜなら独自暗号方式を提案してくる人は暗号化の専門家でないことがほとんどだからだ。

Facebook Messenger が使ってる
-----------------------------

もともと買収した beluga が使っていた。ただ MQTT 以外も使っているようで、全てが MQTT というのは微妙になっている。

詳細は正直、もうわからないという印象。

パーミッションがない
--------------------

MQTT の仕様にはない。ただし、ほとんどのプロダクトが実装している。実装方法は自由なので、それぞれ。

パスワードが生
--------------

ユーザ名とパスワードは生のまま流される。というかここは MQTT Broker 依存。ただ、基本は生データ。

MQTT 規格がロイヤリティフリー
-----------------------------

仕様が曖昧なところが多いので、実装するときはかなり想像しなければいけない。
きっちりしてる仕様では無い。

セッションは無期限で保持
----------------------------

仕様は無期限。ただ現実はそんなことは受け入れられないので、定期的に消したりする仕組みが必要。

リテインは無期限で保持
----------------------------

仕様は無期限。ただ現実はそんなことは受け入れられないので、定期的に消したりする仕組みが必要。

QoS 1-2 は **絶対に届ける**
---------------------------

仕様は絶対。ただ現実にはそんなことは無理なので、どこかで落としどころを探す。

- リトライ回数
- リトライ間隔
- 永続化

色々方法が考えられる。

Amazon が MQTT 系の企業を買収した
---------------------------------

http://2lemetry.com/ という会社が Amazon に買収された。

この会社は MQTT 専業の IoT プラットフォーム会社だ。

Pricing | 2lemetry http://2lemetry.com/pricing/

ここを見て貰えれば QoS が書いていたりするのでわかるだろう。

センサー用には重そう
--------------------

TCP や TLS over TCP が重いというのであれば、そう。
MQTT-SN という no-IP または UDP 向けのプロトコルが別途用意されている。

http://mqtt.org/new/wp-content/uploads/2009/06/MQTT-SN_spec_v1.2.pdf

https://eclipse.org/paho/clients/c/embedded-sn/

Paho でも Client を用意していたりする。

MQTT-SN には暗号化の概念はないので要注意。


3.1 の仕様書を参考にした
------------------------

最新版は 3.1.1 だが 3.1 の方が情報が多いので、致し方ない。

ただ 3.1 と 3.1.1 は **互換性がない** ので注意。そもそもプロトコルバージョンやプロトコル判定に使用される文字列が異なる。

基本的にブローカーは 3.1 と 3.1.1 の両方を実装することが多いので、今のうちは大丈夫。

参照にするときは OASIS 公式の 3.1.1 を参照するのが良い

MQTT Version 3.1.1 http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/mqtt-v3.1.1.html


今後
====

色々 MQTT 関係で話を聞いているがどちらの方向に向かっていくのかを **自分の考えとして** まとめてみた。

- 解析はしらない
- ストレージはしらない

ここに書くのは末端のデバイスの **通信経路** から MQTT ブローカーまで。

とにかく MQTT ブローカーにデータを送るところまでの距離が長いので、その辺を色々解決していきたい。

MQTT-SN
-------

**センサー系についてはど素人ですので、間違ってたら突っ込みお願いします**

MQTT-SN が今後センサーからのデータを運ぶ仕組みとして活躍してくれるはずだ。
そもそもなぜ MQTT-SN が必要なのか。たんにセンサーが一つだけではないパターンが多く出てくると考えているからだ。

後述する TWE-Lite を例に出させて頂くと、ワイヤレスエンジン + センサー数種という構成が増えてくるのは目に見えている。
そのため、様々なセンサーデータを運ぶプロトコルが必要になってくる。

`Paho - Open Source messaging for M2M <https://eclipse.org/paho/clients/c/embedded-sn/>`_

Paho からも MQTT-SN クライアントは出ているので、ワイヤレスエンジン側にこれを実装することで解決する。

センサー用と来るとしたら ZigBee がくると見ている。

- MQTT-SN over ZigBee

MQTT ゲートウェイはまずは ZigBee に対応していれば十分だろう。

ZigBee::

    センサー -> MQTT-SN over ZigBee -> MQTT-GW -> MQTT over TLS -> MQTT Broker

ZigBee
^^^^^^

`東京コスモス電機 ワイヤレス事業部 - TOCOS-WIRELESS.COM <http://tocos-wireless.com/jp/>`_

ZigBee は色々あると思うが、電池面を考えるとこの TOCOS シリーズがとてもスゴイ。

TWE-Lite はコイン型電池で 30 秒に一回のメッセージ送信すると 10 年程度電池が持つらしい ... 。

電波が飛ぶのは 1km 程度と、かなり飛ぶ。電波が強いやつは 4km 程度飛ぶらしい。

- `東京コスモス電機 TWE-Lite:1円玉より小さいZigBee対応無線モジュール、通信距離は1km - EDN Japan <http://ednjapan.com/edn/articles/1305/08/news086.html>`_
- `ASCII.jp:ZigBee無線通信基板「TWE-Lite」を切手大の太陽電池で動作させる電源モジュール <http://ascii.jp/elem/000/000/905/905450/>`_

MQTT-SN の課題
^^^^^^^^^^^^^^

- MQTT 3.1.1 に追従していない
- セキュリティの仕組みが定義されていない

  - 基本的には通信経路で頑張る、これは MQTT が TLS で頑張るのと一緒
  - ZigBee は公開鍵+共通鍵の仕組みが既にある
  
    - ぐぐったレベルなので信頼してはならない

MQTT-SN の暗号化
----------------

どうやら MQTT-SN over DTLS を採用する流れが来ているようだ。ただ、確定とかではない。