对于iot 来说,设备上的上下线事件非常关键并重要,怎对事件的监听呢?
在mqtt协议中,存在LTW(Last Will and Testament)遗言机制,该机制只能捕捉客户端异常离线的通知,而无法获取正常通过disconnect断开连接的通知。
LTW(Last Will and Testament)遗言机制
客户端在连接到Mqtt服务器时,需指定will topic和will message遗言信息,
之后若在客户端异常断开(弱网络、服务被终止,而非正常disconnet)时会由mqtt服务器主动向will topic发送will message,
此时其他监听will topic的用户即可获得客户端离线的will遗言通知;
而在Emqx,存在系统主题订阅,其中的系统主题
上线通知:
$SYS/brokers/${node}/clients/${clientId}/connected
下线通知
$SYS/brokers/${node}/clients/${clientId}/disconnected
LWT和Emqx SYS主题通知效果对比:
机制\事件 | connect | disconnect | 异常disconnect |
---|---|---|---|
$SYS/brokers/${node}/clients/${clientId}/connected | ✔ | ||
$SYS/brokers/${node}/clients/${clientId}/disconnected | ✔ | ✔ | |
LWT | ✔ |
通过实际测试对比发现,LWT仅支持异常disconnect的通知,而emqx的$SYS/brokers/${node}/clients/${clientId}/disconnected系统主题可以捕捉到所有离线通知(主动diconnect、异常disconnect),即emqx的系统主题即可完整支持客户端上线、下线监听的需求。可以根据实际的需求选择适当的机制。
emqx中使用上下线事件:
connected
事件消息的 Payload 解析成 JSON 格式如下:
{
"username":"undefined",
"ts":1582687922392,
"sockport":1883,
"proto_ver":5,
"proto_name":"MQTT",
"keepalive":300,
"ipaddress":"127.0.0.1",
"expiry_interval":0,
"connected_at":1582687922,
"connack":0,
"clientid":"emqtt-8348fe27a87976ad4db3",
"clean_start":true
}
disconnected
事件消息的 Payload 解析成 JSON 格式如下:
{
"username":"undefined",
"ts":1582688032203,
"reason":"tcp_closed",
"disconnected_at":1582688032,
"clientid":"emqtt-8348fe27a87976ad4db3"
}
当然在开发的时候我们订阅两个主题比较麻烦,在这里我们可以采用主题通配符模式直接订阅一个主题即可:
$SYS/brokers/+/clients/#
内置 ACL设置:
内置 ACL 通过文件设置规则,使用上足够简单轻量,适用于规则数量可预测、无变动需求或变动较小的项目。
ACL 规则文件:
etc/acl.conf
内置 ACL 优先级最低,可以被 ACL 插件覆盖,如需禁用全部注释即可。规则文件更改后需重启 EMQ X 以应用生效。
定义 ACL:
内置 ACL 是优先级最低规则表,在所有的 ACL 检查完成后,如果仍然未命中则检查默认的 ACL 规则。
该规则文件以 Erlang 语法的格式进行描述:
%% 允许 "dashboard" 用户 订阅 "$SYS/#" 主题
{allow, {user, "dashboard"}, subscribe, ["$SYS/#"]}.
%% 允许 IP 地址为 "127.0.0.1" 的用户 发布/订阅 "#SYS/#","#" 主题
{allow, {ipaddr, "127.0.0.1"}, pubsub, ["$SYS/#", "#"]}.
%% 拒绝 "所有用户" 订阅 "$SYS/#" "#" 主题
{deny, all, subscribe, ["$SYS/#", {eq, "#"}]}.
%% 允许其它任意的发布订阅操作
{allow, all}.
- 第一条规则允许客户端发布订阅所有主题
- 第二条规则禁止全部客户端订阅
$SYS/#
与#
主题 - 第三条规则允许 ip 地址为
127.0.0.1
的客户端发布/订阅$SYS/#
与#
主题,为第二条开了特例 - 第四条规则允许用户名为
dashboard
的客户端订阅$SYS/#
主题,为第二条开了特例
可知,默认的 ACL 主要是为了限制客户端对系统主题 $SYS/#
和全通配主题 #
的权限。
如果想监听系统主题上下线,需要如下配置:
{allow, all, subscribe, ["$SYS/brokers/+/clients/#"]}.
重新加载acl文件(或者重启emqx服务)
当然在开发的时候我们订阅两个主题比较麻烦,在这里我们可以采用主题通配符模式直接订阅一个主题即可:
$SYS/brokers/+/clients/#
这样比较方便,只不过需要我们在回调函数中进行Topic主题的区分就可以了,例如:
/**
* subscribe后得到的消息会执行到这里面
*/
@Override
public void messageArrived(String topic, MqttMessage message) throws Exception {
String msg = new String(message.getPayload());
try {
JSONObject jsonObject = JSON.parseObject(msg);
String clientId = String.valueOf(jsonObject.get("clientid"));
if (topic.endsWith("disconnected")) {
log.info("客户端已掉线:{}",clientId);
} else {
log.info("客户端已上线:{}",clientId);
}
} catch (JSONException e) {
log.error("JSON Format Parsing Exception : {}", msg);
}
}