はじめに
今回は狭いテーマですが、DisconnectのReasonのひとつである、"Disconnect with Will Message"について確認します。
Reason Code "Disconnect with Will Message"
MQTT3.1.1では異常切断のみWillが発火しましたが、正常切断でも発火してほしい場合があります。MQTT5では、DisconnectのReasonにDisconnectwith Will Message設定すると、異常切断ではなくてもWillを発火させることができます。
規格における、Disconnectwith Will Messageについての記述
The Client wishes to disconnect but requires that the Server also publishes its Will Message.
実践
今回はサーバ側にmosquittoを利用します。
クライアント側からDisconnectの際にReasonを設定してWillが発火することを確認します。
サーバ側 mosquito
下記を参考にdocker上でmqttブローカーを立ち上げる
https://qiita.com/kino15/items/0ef252b10100b2ee2f05
クライアント側 paho
import paho.mqtt.client as mqtt
import threading
import time
from paho.mqtt.packettypes import PacketTypes
from paho.mqtt.reasoncodes import ReasonCode
BROKER = "localhost"
TOPIC = "test/topic"
WILL_TOPIC = "test/will"
# ======================
# Subscriber
# ======================
def on_message(client, userdata, msg):
print(f"[受信] {msg.topic}: {msg.payload.decode()}")
def on_subscribe(client, userdata, mid, granted_qos, properties=None):
print(f"[SUBACK] mid={mid}, qos={granted_qos}")
def subscriber():
sub = mqtt.Client(
client_id="sub-client",
protocol=mqtt.MQTTv5,
callback_api_version=mqtt.CallbackAPIVersion.VERSION2
)
sub.on_message = on_message
sub.on_subscribe = on_subscribe
sub.connect(BROKER, 1883, 60)
sub.subscribe(TOPIC)
sub.subscribe(WILL_TOPIC)
print("[Subscriber] Start")
# ★ スレッドでループ開始
sub.loop_start()
# このまま待機
while True:
time.sleep(1)
# ======================
# Publisher
# ======================
def publisher_with_will_disconnect():
pub = mqtt.Client(
client_id="pub-client",
protocol=mqtt.MQTTv5,
callback_api_version=mqtt.CallbackAPIVersion.VERSION2
)
# Will 設定
pub.will_set(
WILL_TOPIC,
"pub offline via disconnect-with-will",
qos=1,
retain=False
)
pub.connect(BROKER, 1883, 10)
for i in range(3):
msg = f"Hello {i}"
print(f"[送信] {msg}")
pub.publish(TOPIC, msg)
time.sleep(1)
print("\nPublisher が DISCONNECT with Will Message を送信します (0x04)\n")
# ★ ReasonCode の value を直接書き換える方式
rc = ReasonCode(PacketTypes.DISCONNECT)
rc.value = 0x04 # Disconnect with Will Message
pub.disconnect(reasoncode=rc)
print("[Publisher] DISCONNECT(0x04) 送信完了")
# ======================
# メイン
# ======================
if __name__ == "__main__":
t = threading.Thread(target=subscriber, daemon=True)
t.start()
time.sleep(1)
publisher_with_will_disconnect()
print("Will を受信するのを待っています...\n")
time.sleep(10)
Terminal
~/qiita/client]$uv run disconnect_will.py
[Subscriber] Start
[SUBACK] mid=1, qos=[ReasonCode(Suback, 'Granted QoS 0')]
[SUBACK] mid=2, qos=[ReasonCode(Suback, 'Granted QoS 0')]
[送信] Hello 0
[受信] test/topic: Hello 0
[送信] Hello 1
[受信] test/topic: Hello 1
[送信] Hello 2
[受信] test/topic: Hello 2
Publisher が DISCONNECT with Will Message を送信します (0x04)
[Publisher] DISCONNECT(0x04) 送信完了
Will を受信するのを待っています...
[受信] test/will: pub offline via disconnect-with-will
[~/qiita/client]$
まとめ
必ず切断で発火するようになり、便利そうです。
著作権情報
Copyright © OASIS Open 2014. All Rights Reserved.
Available at: https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html
Copyright © OASIS Open 2019. All Rights Reserved.
Available at: https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html