MQTT Last Will 설정
연결할 때 브로커에 등록한 Will 메시지와 retain을 결합하여 online/offline 상태 토픽을 구성한다.
Will 메시지의 필요성
전원이 차단되거나 네트워크가 단절된 클라이언트는 연결 종료를 알리는 메시지를 직접 전송할 수 없다. 다른 클라이언트는 그 장비가 전송할 데이터가 없어 메시지가 없는 것인지, 장애가 발생한 것인지 구분하지 못한다. Will 메시지(유언 메시지)는 이 알림을 브로커가 대신 발행하게 한다.
동작 방식
클라이언트는 CONNECT 패킷에 Will의 토픽, 페이로드, QoS, retain을 포함하여 전송한다. 브로커는 이를 보관했다가, 연결이 정상 절차 없이 닫히면 발행한다.
- 브로커가 네트워크 오류를 감지한 경우
- keep alive 시간 내에 클라이언트가 어떤 패킷도 전송하지 않은 경우
- 클라이언트가 DISCONNECT를 전송하지 않고 연결을 닫은 경우
브로커는 keep alive의 1.5배 동안 패킷이 없으면 연결을 닫는다. keep alive가 30초라면 장애가 발생한 장비를 감지하는 데 최대 약 45초가 소요된다.
클라이언트가 정상 DISCONNECT를 전송하고 연결을 종료하면 브로커는 Will을 폐기하고 발행하지 않는다.
online/offline 상태 토픽
Will에는 offline을 지정하고, 연결 직후에는 같은 토픽에 online을 retain으로 발행한다. 이후에 구독한 클라이언트도 마지막 상태를 즉시 수신한다.
import paho.mqtt.client as mqtt
STATUS = "plant/line1/robot3/connection"
def on_connect(client, userdata, flags, reason_code, properties):
if reason_code == 0:
# 연결될 때마다 online을 올린다
client.publish(STATUS, "online", qos=1, retain=True)
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="robot3")
client.on_connect = on_connect
# connect()보다 먼저 불러야 적용된다
client.will_set(STATUS, "offline", qos=1, retain=True)
client.connect("broker.example", 1883, keepalive=30)
client.loop_forever()
정상 종료 때는 Will이 발행되지 않으므로, disconnect()를 호출하기 전에 offline을 직접 발행한다.