【MQTT】为什么我收不到“遗嘱消息”呢?

日前,有开发者在交流群中提问:我按官方的MQTT api,  “遗嘱”效果是反的:掉线时没有消息,上线时反而有消息。本文就来讲解一下,什么情况使用遗嘱消息,什么情况下会触发遗嘱,以及如何避...

日前,有开发者在交流群中提问:

我按官方的MQTT api,  “遗嘱”效果是反的:掉线时没有消息,上线时反而有消息。

本文就来讲解一下,什么情况使用遗嘱消息,什么情况下会触发遗嘱,以及如何避免它产生的误判。


什么是遗嘱

顾名思义,就是dying message。该“内定”的消息,是MQTT Client 连接 MQTT Broker 时定义的,Broker 记录下该消息。

其后,不论任何情况下,MQTT Client 与 MQTT Broker 之间的连接发生错误,MQTT Broker都会自动发出该消息到对应的主题。

典型的“异常情况”有:

  1. MQTT Client 没有上报 close 就和 MQTT Broker 断开连接;
  2. 多个 keepalive 周期内没有心跳包上报;
  3. 其他相同 ClientID 登录 MQTT Broker 时。

也就是说,灵活的使用遗嘱,可以更方便的应对设备通信时发生的各种异常情况(如模块掉线、设备被盗、流量卡欠费、网咯信号差等等)。


如何启用MQTT遗嘱

默认的MQTT例程中,没有遗嘱的相关内容。不过不用担心,MQTT 已有相关API,敬请查阅:

https://wiki.openluat.com/doc/luatApi/#mqttclientclientid-keepalive-username-password-cleansession-will-version

示例代码:

require "mqtt"
module(..., package.seeall)

-- 这里请填写修改为自己的IP和端口
local host, port = "lbsmqtt.airm2m.com", 1884

sys.taskInit(function()
    while true do
        while not socket.isReady() do sys.wait(1000) end
        local mqttc = mqtt.client(misc.getImei(), 300, "username", "password", nil, {qos=0, retain=0, topic="/willmsg", payload=misc.getImei().."device_conn_err"}, "3.1")
        while not mqttc:connect(host, port) do sys.wait(2000) end
        if mqttc:subscribe(string.format("/device/%s/req", misc.getImei())) then
            if mqttc:publish(string.format("/device/%s/report", misc.getImei()), "test publish " .. os.time()) then
                while true do
                    local r, data, param = mqttc:receive(120000, "pub_msg")
                    if r then
                        log.info("这是收到了服务器下发的消息:", data.payload or "nil")
                    elseif data == "pub_msg" then
                        log.info("这是收到了订阅的消息和参数显示:", data, param)
                        mqttc:publish(string.format("/device/%s/resp", misc.getImei()), "response " .. param)
                    elseif data == "timeout" then
                        log.info("这是等待超时主动上报数据的显示!")
                        mqttc:publish(string.format("/device/%s/report", misc.getImei()), "test publish " .. os.time())
                    else
                        break
                    end
                end
            end
        end
        mqttc:disconnect()
    end
end)

-- 测试代码,用于发送消息给socket
sys.taskInit(function()
    while true do
        sys.publish("pub_msg", "11223344556677889900AABBCCDDEEFF" .. os.time())
        sys.wait(180000)
    end
end)

后端程序的 MQTT Client 订阅 “/willmsg” 这个主题。

当该某 MQTT Client 和 MQTT Broker 连接出错时,Broker 就会自动向 “/willmsg” 主题下发内容为 “868570000000000device_conn_err”(例) 的消息。


为什么我没有收到遗嘱

通常,这是因为测试时,MQTT Client “掉线”时间不够长造成的。测试中,至少要等3个 Keepalive 周期,才会收到 MQTT Broker 下发的内容。

如果长时间测试仍没有收到,那么可能是如下原因:

  1. 模块代码错误,或者网络错误等其他情况,或者没有成功连接到 MQTT Broker;
  2. 后台订阅的主题,和模块的遗嘱不是同一个主题;
  3. MQTT Broker 不支持遗嘱;


为什么我收到了好多次遗嘱

通常是因为模块端频繁掉线或者重连,MQTT Broker 认为上一个 MQTT Client 已经阵亡了,所以向既定主题发布了对应的消息。

开发者需要根据自己的需求,使用不同的主题和payload,区分遗嘱消息和正常消息,并做好对应的处理。



  • 发表于 2020-02-04 08:47
  • 阅读 ( 3144 )

你可能感兴趣的文章

相关问题

0 条评论

请先 登录 后评论
不写代码的码农
技术销售Delectate

43 篇文章

作家榜 »

  1. 技术销售Delectate 43 文章
  2. 陈夏 26 文章
  3. 国梁 24 文章
  4. miuser 21 文章
  5. 晨旭 20 文章
  6. 朱天华 19 文章
  7. 金艺 19 文章
  8. 杨奉武 18 文章