MQTT TopicとWildcardの初心者向けガイド

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/763141/b008b130-3871-c589-7ec6-c957d8277cda.png)

[EMQブログにご覧になってください](https://www.emqx.com/ja/blog/advanced-features-of-mqtt-topics)

**目次**

- [MQTT ワイルドカード](https://www.emqx.com/ja/blog/advanced-features-of-mqtt-topics#mqtt)
- [$で始まるトピック](https://www.emqx.com/ja/blog/advanced-features-of-mqtt-topics#始)
- [さまざまなシナリオのトピック](https://www.emqx.com/ja/blog/advanced-features-of-mqtt-topics#-4)
- [MQTT トピックに関するよくある質問](https://www.emqx.com/ja/blog/advanced-features-of-mqtt-topics#mqtt-関-質問)

MQTT トピックは、メッセージを識別してルーティングするために[MQTT プロトコル](https://www.emqx.com/ja/blog/the-easiest-guide-to-getting-started-with-mqtt)で使用される文字列です。これは、MQTT パブリッシャーとサブスクライバー間の通信における重要な要素です。[MQTT パブリッシュ/サブスクライブ モデル](https://www.emqx.com/ja/blog/mqtt-5-introduction-to-publish-subscribe-model)では、パブリッシャーは特定のトピックにメッセージを送信し、サブスクライバーはそれらのトピックをサブスクライブしてメッセージを受信できます。

Kafka や Pulsar などの他のメッセージング システムのトピックと比較して、MQTT トピックは事前に作成する必要はありません。**クライアントは、サブスクライブまたはパブリッシュ時にトピックを自動的に作成するため、トピックを削除する必要はありません。**

以下は、単純な MQTT パブリッシュおよびサブスクライブのフローです。APP 1 が`sensor/2/temperature`トピックをサブスクライブすると、このトピックに公開しているセンサー 2 からメッセージを受信します。

![MQTT Publish Subscribe](https://assets.emqx.com/images/0c35bfdb730f1d29b7f1b7a249c62f8b.png?imageMogr2/thumbnail/1520x)

## トピック

トピックは、MQTT プロトコルでのメッセージ ルーティングの基礎となる UTF-8 でエンコードされた文字列です。`/`通常、トピックはレベル分けされ、レベル間はスラッシュで区切られます。これは、次のような URL パスに似ています。

```awk
chat/room/1
sensor/10/temperature
sensor/+/temperature
sensor/#
```

許可されている場合でも、/chat や chat/ のように、トピックが / で始まったり終わったりするのは、通常お勧めしません。

## MQTT ワイルドカード

MQTT ワイルドカードは、サブスクリプションにのみ使用でき、公開には使用できない特殊なタイプのトピックです。クライアントはワイルドカード トピックをサブスクライブして、一致する複数のトピックからメッセージを受信できるため、各トピックを個別にサブスクライブする必要がなくなり、オーバーヘッドが削減されます。`+`MQTT は、 (シングルレベル) と`#`(マルチレベル)の 2 種類のワイルドカードをサポートします。

### 単一レベルのワイルドカード

`+`(U+002B) は、1 つのトピック レベルのみに一致するワイルドカード文字です。単一レベルのワイルドカードを使用する場合、次のように単一レベルのワイルドカードがレベル全体を占める必要があります。

```pgsql
"+" is valid
"sensor/+" is valid
"sensor/+/temperature" is valid
"sensor+" is invalid (does not occupy an entire level)
```

クライアントがトピックをサブスクライブすると`sensor/+/temperature`、次のトピックからメッセージを受信します。

```awk
sensor/1/temperature
sensor/2/temperature
...
sensor/n/temperature
```

ただし、次のトピックとは一致しません。

```bash
sensor/temperature
sensor/bedroom/1/temperature
```

### マルチレベルのワイルドカード

`#`(U+0023) は、トピック内の任意の数のレベルに一致するワイルドカード文字です。マルチレベルのワイルドカードを使用する場合は、レベル全体を占め、トピックの最後の文字である必要があります。次に例を示します。

```pgsql
"#" is valid, matches all topics
"sensor/#" is valid
"sensor/bedroom#" is invalid (+ or # are only used as a wildcard level)
"sensor/#/temperature" is invalid (# must be the last level)
```

## $で始まるトピック

### システムトピックス

で始まるトピックは、`$SYS/`主に MQTT ブローカーの実行ステータス、統計、クライアントのオンライン/オフライン イベントなどに関するメタデータを取得するために使用されるシステム トピックです。`$SYS/`トピックは MQTT 仕様では定義されていません。ただし、ほとんどの[MQTT ブローカーは](https://www.emqx.com/ja/blog/the-ultimate-guide-to-mqtt-broker-comparison)この[推奨事項](https://github.com/mqtt/mqtt.org/wiki/SYS-Topics)に従っています。

たとえば、[EMQX は](https://www.emqx.io/ja)、次のトピックを通じてクラスターのステータスの取得をサポートしています。

| トピック                        | 説明                        |
| :------------------------------ | :-------------------------- |
| `$SYS/brokers`                  | EMQXクラスターノードリスト  |
| `$SYS/brokers/${node}/version`  | EMQX ブローカーのバージョン |
| `$SYS/brokers/${node}/uptime`   | EMQX ブローカーの起動時間   |
| `$SYS/brokers/${node}/datetime` | EMQX ブローカー時間         |
| `$SYS/brokers/${node}/sysdescr` | EMQX ブローカーの説明       |

EMQX は、クライアントのオンライン/オフライン イベント、統計、システム監視、アラームなどの豊富なシステム トピックもサポートしています。詳細については、[EMQX システム トピックの](https://www.emqx.io/docs/en/v5.0/advanced/system-topic.html)ドキュメントを参照してください。

### 共有サブスクリプション

共有サブスクリプションは MQTT 5.0 の機能であり、複数のサブスクライバ間の負荷分散を実現するサブスクリプション方式です。共有サブスクリプションのトピックは $share で始まります。

> *MQTT プロトコルは 5.0 で共有サブスクリプションを追加しましたが、EMQX は MQTT 3.1.1 から共有サブスクリプションをサポートしています。*

次の図では、3 人のサブスクライバが`$share/g/topic`共有サブスクリプション方式を使用して同じトピックにサブスクライブしています。ここで、`topic`はサブスクライブしている実際のトピック名で、パブリッシャーはメッセージを にパブリッシュします`topic`が、には*パブリッシュしません*`$share/g/topic`。

![MQTT Shared Subscriptions](https://assets.emqx.com/images/c248e9334ff6d32cbec0ed71cde98b1f.png?imageMogr2/thumbnail/1520x)

さらに、EMQX は、`$queue`MQTT 3.1.1 での共有サブスクリプション プレフィックスの使用もサポートしています。これは共有サブスクリプションの特殊なケースであり、すべてのサブスクライバが 1 つのグループに含まれるのと同じです。

共有サブスクリプションの詳細については、[EMQX 共有サブスクリプションの](https://www.emqx.io/docs/en/v5.0/advanced/shared-subscriptions.html)ドキュメントを参照してください。

## さまざまなシナリオのトピック

### スマートホーム

たとえば、センサーを使用して寝室、リビングルーム、キッチンの温度、湿度、空気の質を監視します。次のトピックをデザインできます。

- `myhome/bedroom/temperature`
- `myhome/bedroom/humidity`
- `myhome/bedroom/airquality`
- `myhome/livingroom/temperature`
- `myhome/livingroom/humidity`
- `myhome/livingroom/airquality`
- `myhome/kitchen/temperature`
- `myhome/kitchen/humidity`
- `myhome/kitchen/airquality`

`myhome/bedroom/+`次に、寝室の温度、湿度、空気質のデータを取得するトピック、`myhome/+/temperature`3 つの部屋すべての温度データを取得するトピック、および`myhome/#`すべてのデータを取得するトピックをサブスクライブできます。

### 充電パイル

- `ocpp/cp/cp001/notify/bootNotification`

  課金パイルがオンラインのときに、このトピックにオンライン リクエストを発行します。

- `ocpp/cp/cp001/notify/startTransaction`

  このトピックに課金リクエストを発行します。

- `ocpp/cp/cp001/reply/bootNotification`

  課金パイルがオンラインになる前に、オンライン応答を受け取るためにこのトピックをサブスクライブする必要があります。

- `ocpp/cp/cp001/reply/startTransaction`

  課金パイルが課金リクエストを開始する前に、このトピックをサブスクライブして課金リクエストの応答を受信する必要があります。

### インスタントメッセージング

- `chat/user/${user_id}/inbox`

  **1 対 1 チャット**: ユーザーはオンラインになった後にこのトピックを購読し、友達からメッセージを受け取ります。友人に返信するときは、トピックの user_id を友人の ID に置き換えるだけです。

- `chat/group/${group_id}/inbox`

  **グループ チャット**: ユーザーがグループに正常に参加した後、トピックを購読してグループのメッセージを取得できます。

- `req/user/${user_id}/add`

  **友達を追加**: このトピックに友達リクエストを公開します (user_id は友達の ID)。

  **友達リクエストを受信する**: 他のユーザーから友達リクエストを受信するには、このトピック (user_id は購読者の ID) を購読します。

- `resp/user/${user_id}/add`

  **友達リクエストへの返信を受信する**: 友達を追加する前に、ユーザーはリクエストの結果を受け取るためにこのトピック (user_id は購読者の ID) を購読する必要があります。

  **友達リクエストに返信**: 友達リクエストを承認するかどうかについて、このトピック (user_id は友達の ID) にメッセージを送信します。

- `user/${user_id}/state`

  **ユーザー ステータス**: このトピックを購読して、友達のオンライン ステータスを取得します。

## MQTT トピックに関するよくある質問

### MQTT トピックの最大レベルと長さはどれくらいですか?

MQTT トピックは UTF-8 でエンコードされた文字列であり、**65535 バイトを超えてはなりません**。ただし、実際には、より短いトピック名を使用し、レベルを少なくすると、リソースの消費が少なくなります。

「できる訳」という観点で、これ以上のトピックレベルを使用しないようにしてください。たとえば、`my-home/room1/data`は よりも良い選択です`my/home/room1/data`。

### トピック数に制限はありますか?

メッセージ サーバーが異なれば、トピック数の制限も異なります。現在、EMQX のデフォルト構成にはトピックの数に制限はありませんが、トピックの数が増えると、より多くのサーバー メモリが使用されます。

MQTT ブローカーに接続されているデバイスの数が多いことを考慮して、クライアントがサブスクライブするトピックは 10 個以下にすることをお勧めします。

### ワイルドカード サブスクリプションはパフォーマンスを低下させますか?

メッセージをワイルドカード サブスクリプションにルーティングする場合、ブローカーは非ワイルドカード トピックよりも多くのリソースを必要とする場合があります。ワイルドカード サブスクリプションを回避できる場合は、これが賢明な選択です。

これは、MQTT メッセージ ペイロードに対してデータ スキーマがどのようにモデル化されるかに大きく依存します。

たとえば、パブリッシャが`device-id/stream1/foo`と にパブリッシュし`device-id/stream1/bar`、サブスクライバが両方にサブスクライブする必要がある場合、サブスクライブすることができます`device-id/stream1/#`。おそらく、より良い代替案は、名前空間の foo および bar 部分をペイロードにプッシュして、1 つのトピックのみにパブリッシュし、`device-id/stream1`サブスクライバーはこの 1 つのトピックのみをサブスクライブすることです。

### 通常トピックとワイルドカードトピックの重複サブスクリプションのメッセージはどのように受信されますか?

たとえば、クライアントが`#`と`test`トピックの両方をサブスクライブしている場合、クライアントは に公開するときに 2 つの重複メッセージを受信します`test`。これは、MQTT ブローカーの実装によって異なります。EMQX は、一致したサブスクリプションごとにメッセージを送信します。したがって、重複が発生する可能性があります。ただし、ユーザーは MQTT 5.0 サブスクリプション識別子を利用してメッセージ ソースを区別し、識別子に基づいてクライアントでそのような重複メッセージを処理できます。

### 共有サブスクリプションと通常のサブスクリプションで同じトピックをサブスクライブできますか?

はい、ただしお勧めしません。

MQTT 仕様に従って、複数のサブスクリプションにより複数の (重複した) メッセージ配信が行われます。

### MQTT トピックのベスト プラクティスは何ですか?

- すべてのトピックを購読するために使用しないでください`#`。
- トピックは、や など`/`ので始まったり終わったりしないでください。`/chatchat/`
- トピック内ではスペースや非 ASCII 文字を使用しないでください。
- トピック レベル内の単語 (またはキャメル ケース) を接続するには、`_`または を使用します。`-`
- トピックのレベルを下げるようにしてください。
- ワイルドカード トピックの使用を避けるために、メッセージ データ スキーマをモデル化するようにしてください。
- ワイルドカードを使用する場合は、より固有のトピック レベルをルートに近づけるようにしてください。たとえば、`device/00000001/command/#`よりも良い選択です`device/command/00000001/#`。