chakokuのブログ(rev4)

テック・コミック・DTM・・・ごくまれにチャリ

AWS IoT Coreのメッセージ送受信にかかる時間を調査

背景:対戦型ゲームを改良する。ボタン操作をMQTTメッセージを使って共有したい
課題:ゲームのリアルタイム性を確保するため、なるべく配信にかかる時間の短いMQTTブローカを選ぶ必要あり
結論:やはり(有料の)AWS IoT Coreが断然早かった(往復にかかる時間が二桁msecのオーダ)
詳細:

なるべく遅延なくメッセージが配信されるブローカを使いたい。前回無料のPublic MQTT Brokerサービスをざっと調べたが、 EMQXの160msecが最短であった。AWS IoT Coreが高速だろうとは思っていたが有償だったので、前回は採用しなかった。今回ゲームのアーキを見直す*1にあたりAWS IoT Coreはメッセージ配信にどれぐらい時間がかかるのかを調べた。結果は以下

Received message on topic 'sensor/device01': 26 msec
Received message on topic 'sensor/device01': 30 msec
Received message on topic 'sensor/device01': 30 msec
Received message on topic 'sensor/device01': 31 msec
Received message on topic 'sensor/device01': 29 msec
Received message on topic 'sensor/device01': 29 msec
Received message on topic 'sensor/device01': 24 msec
Received message on topic 'sensor/device01': 25 msec
Received message on topic 'sensor/device01': 23 msec
Received message on topic 'sensor/device01': 26 msec
Received message on topic 'sensor/device01': 29 msec
Received message on topic 'sensor/device01': 27 msec
Received message on topic 'sensor/device01': 42 msec
Received message on topic 'sensor/device01': 25 msec
Received message on topic 'sensor/device01': 28 msec
Received message on topic 'sensor/device01': 23 msec
Received message on topic 'sensor/device01': 24 msec
Received message on topic 'sensor/device01': 25 msec
Received message on topic 'sensor/device01': 25 msec
Received message on topic 'sensor/device01': 29 msec

中央値とか統計で計算するのが面倒なので、これ以上集計しませんが、、、見た目で、だいたい、30msecぐらいで配信可能。無料で一番速かったEMQXの160msecと比べると、5倍ぐらい早い。有料サービスだけあってさすが高性能。
調べるのに使ったソースも一応添付します。(上記計測にはテストプログラムの処理時間も入っています)。より高精度に計測するにはパケットキャプチャ等で計測すべきと思いますが、普通の人がボタン連打した時、MAX10発/秒ぐらいで、周期100mesc前後なので、この程度の計測精度で十分かと。

#!/usr/bin/python3
import os
import time
import datetime
import json
import ssl
import paho.mqtt.client as mqtt
from paho.mqtt.properties import Properties
from paho.mqtt.packettypes import PacketTypes

ENDPOINT = 'a3xxxxxxxxs.iot.ap-northeast-1.amazonaws.com'
PORT = 8883

CA_FILE = './crt/AmazonRootCA1.pem'
CERT_FILE = './crt/16exxxxxxxxxxxxxxx45-certificate.pem.crt'
KEY_FILE = './crt/16exxxxxxxxxxxxxx45-private.pem.key'

TOPIC = "sensor/device01"

is_connected = False

def on_connect(client, userdata, flags, rc, properties=None):
    global is_connected
    if rc == 0:
        print("Connected to AWS IoT Core")
        is_connected = True
        client.subscribe(TOPIC)
        print(f"Subscribed to topic: {TOPIC}")
    else:
        print(f"Failed to connect, return code {rc}")


def on_disconnect(client, userdata, rc, properties=None):
    if rc != 0:
        print("Unexpected disconnection")
    else:
        print("Disconnected from AWS IoT Core")

def on_message(client, userdata, msg):
    global send_time
    try:
        payload = json.loads(msg.payload.decode('utf-8'))
        print(f"Received message on topic '{msg.topic}': ",end='')
        #print(f"Payload: {json.dumps(payload, indent=2)}")
        received_time = datetime.datetime.now()
        elapsed_time = received_time - send_time
        print(int(elapsed_time.microseconds/1000), 'msec')
    except Exception as e:
        print(f"Error processing message: {e}")


send_time = None
def main():
    global is_connected
    global send_time
    client = mqtt.Client(client_id="myClientID", protocol=mqtt.MQTTv5)
    client.tls_set(
        ca_certs = CA_FILE,
        certfile = CERT_FILE,
        keyfile = KEY_FILE,
        cert_reqs = ssl.CERT_REQUIRED,
        tls_version = ssl.PROTOCOL_TLSv1_2
    )
    client.on_connect = on_connect
    client.on_disconnect = on_disconnect
    client.on_message = on_message

    client.connect(ENDPOINT, PORT)
    client.loop_start()

    payload = json.dumps({"message": "hello from paho-mqtt"})
    props = Properties(PacketTypes.PUBLISH)
    props.ContentType = "application/json"
    while not is_connected:
        print('z')
        time.sleep(1)

    for _ in range(20):
        send_time = datetime.datetime.now()
        client.publish(TOPIC, payload, qos=0, properties=props)
        time.sleep(0.5)

    print('closing..')
    time.sleep(0.5)
    client.loop_stop()
    client.disconnect()

if __name__ == "__main__":
    main()

*1:クリックするつどメッセージをpublishする