[AWS IoT Core] MQTT v5 を使用してトピック・エイリアスを実装して見ました

2022.12.02

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

1 はじめに

CX 事業本部のデリバリー部の平内(SIN)です。

AWS re:Invent 2022 で発表された、AWS IoT Core での MQTT v5 対応に反応して、色々入門しています。

今回は、トピック・エイリアスの実装を試して見たいと思います

※ 現時点(2022/12/02)では、AWS で提供される SDK は、MQTT v5 に対応していないため、サンプル作成には、paho.mqtt を使用させて頂きました。

2022/12/05 追記 Python SDK が開発者プレビューで MQTT v5 対応となりました。
参考:https://dev.classmethod.jp/articles/aws-iot-core-mqtt-v5-sdk-for-python

2 Topic Alias

トピック・エイリアスとは、拡張されたプロパティ領域を使用して、トピックを数値に置き換え、ネットワークトラフィックを節約しようというものです。

通信に充分な帯域が確保されている場合、このような機能は必要ないかも知れませんが、比較的、MQTT が採用される、モバイル接続されたデバイス等では有効といなる場面がありそうです。

MQTT v3.1 では、Publish 毎に必ずトピックの指定が必要ですが、トピック・エイリアスを使用することで2回目以降のトピックは 0 バイトに省略できます。

全体の通信の中で、payload 本体に対して、トピック名が、大きい場合や、同一のトピックに対して、多数のデータを Publish する場合などに効果があると言えそうです。

拡張プロパティを使用したトピックの置き換え(数値)は、全部で 2 バイトで表現されます。

3 使用方法

トピック・エイリアスは、同一ネットワークコネクションの間のみで有効です。

トピックとエイリアスを設定すると、それ以降の通信は、エイリアスのみの指定だけでよく、トピックは、0 バイトとすることができます。

トピックを変更したい場合は、トピックとエイリアスを再設定します。

関連するプロパティは、以下の通りです。

数値 名称 意味概説 データ型 適用コマンド
0x22 Topic Alias Maximum Topic Alias の最大値 2Bytes CONNECT, CONNACK
0x23 Topic Alias Topic Alias 値 2Bytes PUBLISH

トピック・エイリアスは、Topic Alias Maximum で設定されている数までしか利用できず、AWS IoT Core のデフォルトの制限は 8(ソフトリミット) となっています。

https://docs.aws.amazon.com/general/latest/gr/iot-core.html

※ クライアントが最大値を超えるトピック エイリアス使用して Publish しようとすると、クライアントは切断されます。

4 実装例

以下が、実装したコードです。

プログラムは IoT Core に接続後、3 回1回トピックを指定した Publish を行い、残りの 2 回は、トピック指定を 0 バイトにしています。

import ssl
import time
import os
import paho.mqtt.client as mqtt
from paho.mqtt.properties import Properties
from paho.mqtt.packettypes import PacketTypes

endpoint = "xxxxxxxxxxxx-ats.iot.ap-northeast-1.amazonaws.com"
port = 8883

dir = os.path.dirname(os.path.abspath(__file__))
certs = {
    "cafile": "{}/certificates/AmazonRootCA1.pem".format(dir),
    "certfile": "{}/certificates/client-cert.pem".format(dir),
    "keyfile": "{}/certificates/private-key.pem".format(dir),
}

def main():

    client = mqtt.Client("client_id", protocol = mqtt.MQTTv5)
    client.tls_set(certs["cafile"],
            certfile=certs["certfile"],
            keyfile=certs["keyfile"],
            cert_reqs=ssl.CERT_REQUIRED,
            tls_version=ssl.PROTOCOL_TLSv1_2,
            ciphers=None)
    client.connect(endpoint, port, properties = None)
    client.loop_start()

    properties = Properties(PacketTypes.PUBLISH)
    properties.TopicAlias = 1

    topic = "sensors/123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-1234567890"

    for i in range(100):
        payload = "{}".format(i)
        if(i % 3 == 0):
            client.publish(topic, payload, qos=0, properties=properties)
        else:
            client.publish('', payload, qos=0, properties=properties)
        time.sleep(1)

if __name__ == "__main__":
    main()

5 動作確認

プログラムを実行して、AWS IoT Core のテストコンソールで確認している様子です。全ての通信は、同一のトピックに到着しています。


MQTT 通信の様子を Wireshark で確認してみました。

TCP パケットのサイズが、3 回に 1 回大きくなっていることが確認できます。

TCP のデータ部分です。TLS で暗号化されているので、内容は確認できていませんが、Length の違いは確認できます。

6 最後に

非常に細かい節約のようですが、帯域が極細の場合や、通信費が高額な場合、この辺を意識することは大事だと思います。

ふと、衛星通信をイメージしてしまいました。

7 参考リンク


Introducing new MQTTv5 features for AWS IoT Core to help build flexible architecture patterns
[AWS IoT Core] MQTT v5 を使用してリクエスト・レスポンス パターンを実装して見ました
[AWS IoT Core] MQTT v5 を使用してユーザープロパティを実装して見ました
[AWS IoT Core] MQTT v5 を使用してメッセージ及び、セッション有効期限とクリーンスタートを実装して見ました