글 / MQTT 메시징

MQTT 재연결 후 구독 복구

구독은 세션에 포함되어 세션이 새로 시작되면 삭제되므로 on_connect 내부에서 다시 구독한다.

작성일 · 읽는 시간 2분 · Paho MQTT 2.1 기준

문제 정의

연결이 단절되었다가 재연결된 뒤 메시지가 더 이상 수신되지 않는 경우가 있다. 구독 목록은 브로커가 세션에 저장하는 상태이므로, 세션이 새로 시작되면 구독도 함께 삭제된다. paho-mqtt는 기본 설정에서 연결할 때마다 세션을 새로 시작하며, 단절된 연결만 다시 수립한다.

연결 콜백 내 구독

on_connect는 브로커의 CONNACK을 수신할 때마다 호출된다. 재연결 때도 호출되므로, 구독을 이 콜백 내부에 두면 연결될 때마다 구독이 복구된다.

재연결 때 on_connect에서 구독이 복구되는 순서

pythonsubscriber.py
import paho.mqtt.client as mqtt

TOPICS = [("plant/line1/+/status", 1), ("plant/line1/+/alarm", 1)]


def on_connect(client, userdata, flags, reason_code, properties):
    if reason_code == 0:
        # 재연결할 때도 여기로 들어온다
        client.subscribe(TOPICS)


client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
client.on_connect = on_connect
client.on_message = lambda c, u, m: print(m.topic, m.payload)
client.connect("broker.example", 1883, keepalive=30)
# 연결이 끊기면 알아서 다시 연결한다
client.loop_forever()

connect() 직후에 한 번만 구독하는 코드는 첫 연결에서만 동작한다.

CallbackAPIVersion

paho-mqtt 2.x는 Client의 첫 인자로 콜백 버전을 받는다. VERSION2의 on_connect는 위 코드와 같이 인자 다섯 개를 받는다. VERSION1은 MQTT 3.1.1에서 client, userdata, flags, rc 네 개를 받으며 폐기 예정(deprecated)이다.

2.1에서는 첫 인자를 생략하면 VERSION1로 동작하면서 DeprecationWarning이 발생한다. 1.x처럼 첫 인자 위치에 client id 문자열을 지정하면 ValueError가 발생한다.

세션 유지 시의 처리

세션을 유지하도록 연결한 경우 flags.session_present가 True로 전달될 수 있다. 이는 브로커에 구독이 남아 있음을 의미한다. 이때 다시 구독해도 메시지가 손실되지는 않지만 retained 메시지(브로커가 보관하는 마지막 값)가 한 번 더 전달된다. 이를 방지하려면 False일 때만 구독한다.

flags.session_present 값에 따른 재구독 여부의 분기