【IoT入門】MQTTについて１から丁寧に解説

## MQTTとは何か？
MQTT（エムキューキューティーティー）は、IoT向けのOASIS標準メッセージングプロトコルです。Publish/Subscribeパターンのメッセージングにより、非同期に1対多の通信をすることが可能です。MQTTは、組み込みデバイス、センサー、産業用PLCなどのIoTおよび産業用IoT（IIoT）デバイス間のメッセージングとデータ交換など、メモリの制約が厳しかったり、ネットワーク帯域幅が限られているような環境での利用に適しています

## なぜMQTTを用いるのか？
### 軽量で効率的
MQTTクライアントは非常に小さく、最小限のリソースしか必要としないため、小型のマイクロコントローラーで使用できます。MQTTメッセージのヘッダは小さく、ネットワーク帯域を最適化します（MQTTのヘッダサイズは２バイト〜とHTTPに比べるとかなり軽量になっています）

### 双方向通信
MQTTでは、デバイスとクラウド、クラウドとデバイスの間でメッセージをやり取りすることができます。このため、モノのグループに対して簡単にメッセージをブロードキャストすることができます

### スケーラビリティ
MQTTは、数百万台のIoTデバイスと接続するためのスケーラビリティを備えています

### 信頼性の高いメッセージ配信
IoT のユースケースの信頼性を確保するためにMQTTでは、メッセージごとに到達保証に関するQoS(サービスの品質)を指定することができます

### 信頼性の低いネットワークへの対応
多くのIoTデバイスは、信頼性の低い携帯回線で接続します。MQTTには持続的セッションのサポートにより、クライアントとブローカーの再接続にかかる時間を短縮することができます

### セキュア
MQTTでは、TLSによるメッセージの暗号化や、OAuthなどの最新の認証プロトコルを使ったクライアント認証が簡単に行えます

### 充実したサポート
Pythonなどの言語では、MQTTの実装が簡単にできるようになっています。なので、開発者はあらゆるアプリケーションにおいて、最小限のコーディングで素早く実装することができます

## MQTTの基本的な概念
### メッセージ
MQTT通信の基本となる単位がメッセージです。データかコマンドかを問わず、デバイス間で交換される情報を意味します。メッセージは、それを識別するためのキー情報「トピック」を伴います

### トピック
トピックとは、メッセージ送受信のキーになる情報で、そのメッセージの種類を指定するものです。このトピックをメッセージの属性に加えることで、ブローカーがどのサブスクライバーに受信させるかを判断します

トピックは、1つ以上の**トピックレベル**で構成されます。そして、各トピックレベルはスラッシュ（トピックレベルセパレータ）で区切られます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/270253/7ceff2c2-f3af-5e45-d2bd-879af8b0bdaa.png)

### パブリッシュ（Publish） / サブスクライブ（Subscribe）パターン
MQTTプロトコルは、パブリッシュ／サブスクライブ（Pub/Sub）というパターンを使用してデバイスを接続します。Pub/Subは、イベント駆動型プログラミングのデザインパターンです。メッセージを送信するクライアントを**パブリッシャー（Publisher）**、メッセージを受信するクライアントを**サブスクライバー（Subscriber）**、その仲介をするサーバーを**ブローカー（Broker）** と呼びます。MQTTはメッセージの**トピック**を使って、どのメッセージがどのクライアント (サブスクライバー) に送られるかを決定します

パブリッシャーは宛先（サブスクライバー）を限定せずに情報を発信して、複数のサブスクライバーは、それぞれが関心のあるメッセージだけをトピックで選択して受信することができます

例えば、下記画像で言うと、Mobile DeviceとBackend Systemが`/templeture`というトピックをsubscribeしています。なので、パブリッシャーである湿度計がメッセージの属性値に`/templeture`というトピックをつけてpublishすると、ブローカーを介して、Mobile DeviceとBackend Systemにメッセージが送られます。これがもしMobile Deviceが`/house`のような別のトピック名をsubscribeしている場合は、このメッセージを受信しません

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/270253/bede72d6-f008-16c3-606a-58a424f07219.png)

### ブローカー（Broker）
MQTT通信を仲介するサーバーです。ブローカーはすべてのメッセージの受信、フィルタリング、トピックに関心をもつデバイスの決定、そしてSubscribeしているすべてのクライアントへのメッセージ送信を担います。　OSS（Open Source Software）のMQTT Brokerもたくさんあり、それぞれ実装している機能や仕様に違いがあるので事前に検討が必要です

:::note info
**MQTTの大きな特徴**
従来のクライアント/サーバーアーキテクチャーと異なり、メッセージの発行者と受信者が分離されていることです。パブリッシャーとサブスクライバーはお互いのことを知りません。ブローカーによって発行者と受信者の接続は処理されます
:::

## MQTTの機能
### QoS（Quality of Service）
QoSとは、メッセージの送信者とメッセージの受信者の間の合意で、特定のメッセージの配信保証を定義するものです。MQTTには3つのQoSレベルがあります

#### QoS0
最高1回は配信されます。メッセージが送信先に確実に届くかどうかの保証はされません

#### QoS1
最低1回は配信されます。確実に届くが、メッセージが重複して届く可能性があります

#### QoS2
正確に1回配信されます。重複なく届くことが保証されますが、品質が高い反面、通信回数が増えて遅くなります

### Retained Messages
Publish/SubscribeパターンはSubscribeを開始するまでにPublishされたメッセージは届きません。なので、状況によっては（Publishのインターバルが長い場合など）、Subscribe開始後、無応答のような状況が発生してしまいます。これを避けるために、**Retained Messages**を使用します。すると、ブローカーは最後にPublishされたメッセージを保持して、新しいSubscribeを受けるとそのメッセージを通知します

MQTTプロトコルフォーマットのフラグでは、PublishでRETAINというフラグがあるので、これを設定することでRetained Messagesを使用することが可能です

また、ブローカーは、1つのトピックにつき1つのRetained Messagesのみを保存します

### LWT（Last Will and Testament）
パブリッシャーは、MQTTブローカーと接続したときに **Last Will and Testament（遺言）** という情報を追加することができます。意図しない原因でパブリッシャーと通信できなくなった場合に、ブローカーはLWTに指定されたメッセージをサブスクライバーへ送信します。サブスクライバーはこのメッセージを受信することで、対象のパブリッシャーが意図しない原因で停止していることがわかります。

クライアントが正しい DISCONNECT メッセージで正しく切断された場合、ブローカーは保存された LWT メッセージを破棄します

### Keep Alive
例えばプログラムのバグなどにより、MQTTに利用するコネクションは生きているが、実際の通信ができないような状態（TCPのハーフコネクション問題）に陥ることがあります。この場合、ハーフコネクションの相手がデータを送り続けても、相手側にメッセージが届くことはありません。この問題に対応するため、MQTTプロトコルでは、**ハーフコネクションを判断し、該当するコネクションを切断するKeep Aliveという仕組み**を提供しています

#### Keep Aliveがハーフコネクションを発見する仕組み
MQTTクライアントとMQTTブローカーの接続が確立された後、クライアントは送信する2つのMQTTプロトコルパケットの間隔がKeep Alive値を超えないようにする必要があります。つまり、Keep Aliveの値を超えるとハーフコネクションが発生していると判断します

Keep Alive値を超えてしまうとコネクションが切断されてしまうため、通常時はメッセージの代わりにPINGREQプロトコルパケットを送信します

※ ハーフコネクションだと判断され、コネクションが切断された場合、Last Will and Testamentメッセージを送信します（クライアントが LWT を指定していた場合）

#### Keep Aliveの設定方法
MQTTクライアントがMQTTブローカーとの接続を作成する際に、Keep Aliveフィールドに0以外の値を設定することで、通信当事者間でKeep Aliveを有効にすることができます。Keep Aliveは0～65535の整数で設定します

## おわりに
これでMQTTについての解説は終了です。概念や仕組みの理解を中心に記事を書いたので、次は実際にMQTTを用いて実践して行きたいと思います