在使用python
开发过程中,使用到MQTT
消息订阅机制以实现业务需求。其中一项便是订阅消息的客户端需要收到关于其他客户端的MQTT服务器上线与下线
的消息推送。这里记录一下实现的步骤与注意事项。
操作系统平台与软件版本:
Windows 10
emqx 4.3
实现的步骤主要有:
是否允许匿名登录
由允许改为不允许主要的注意事项:
emqx
的版本区别(emqx 4.3
版本和4.2
以及以前的旧版本的设置方法有区别)emqx 4.2
及以前的版本emqx 4.2
及以前的版本如何更改登录用户名和密码,可通过搜索引擎检索一下,有大量的文档或技术博客有说明,不再赘述。
假设 emqx 安装在 D:\
盘,安装教程,此时,打开系统配置文件,位于d:\emqx\etc\emqx.conf
,以文本的方式打开,修改配置项allow_anonymous
为false
,修改前,系统默认值true
:
## Allow anonymous authentication by default if no auth plugins loaded.
## Notice: Disable the option in production deployment!
##
## Value: true | false
allow_anonymous = false
修改完成后,保存退出。
登录127.0.0.1:18083
,输入默认账户名admin
和默认密码public
登录控制台,在左侧导航栏点击Plugins
(中文界面显示的是插件
),启动emqx_auth_mnesia
即可
在Emqx4.3版本之前存在一个
emqx_auth_username
扩展,可以通过启动emqx_auth_username
插件模块修改其配置实现账号密码验证。
但在Emqx 4.3
版本之后不一样了,官方文档 里有注明emqx 4.3
版本中emqx_auth_clientid
与emqx_auth_usernmae
合并为emqx_auth_mnesia
。emqx_auth_username
模块从此废弃。在Emqx 4.3
版中加载emq_auth_username
插件会报不存在的错误,所以不要再去寻找安装emqx_auth_username
模块了。直接修改emqx_auth_mnesia.conf
模块配置文件,添加账号密码。
找到与插件emqx_auth_mnesia
对应的配置文件,位于D:\emqx\etc\plugins\emqx_auth_mnesia.conf
,添加如下用户名和密码:
## Password hash.
##
## Value: plain | md5 | sha | sha256 | sha512
auth.mnesia.password_hash = sha256
##--------------------------------------------------------------------
## ClientId Authentication
##--------------------------------------------------------------------
auth.client.1.clientid = admin
auth.client.1.password = your_own_password
##--------------------------------------------------------------------
## Username Authentication
##--------------------------------------------------------------------
## Examples:
auth.user.1.username = admin
auth.user.1.password = your_own_password
保存后退出。
打开d:\emqx\etc\acl.conf
,添加一行用户访问配置:
{allow, {user, "admin"}, subscribe, ["$SYS/#"]}.
允许admin
用户订阅系统级消息。保存后退出,重启emqx
服务即可
客户端离线与在线消息属于系统级通知,需要客户端订阅系统预定义的topic
:
离线topic $SYS/brokers/+/clients/+/disconnected
上线topic $SYS/brokers/+/clients/+/connected
上下线topic $SYS/brokers/+/clients/#
示例代码:
def on_connect(client, userdata, flags, rc):
# this method will be called when client connected to server successfully
# TODO do something when this client been notified about successfully connected to server
pass
def on_message(client, userdata, msg: mqtt.MQTTMessage):
# this method will be called when this client get a message under the topic(s) it subscribed, encluding the system message (I assuming you've already configure it properly)
# TODO do something when this client get a message
pass
mqtt_server = "127.0.0.1"
client = mqtt.Client("surveillance_client")
client.username_pw_set("admin", "aoto@123")
# 定义回调方法
client.on_connect = on_connect
client.on_message = on_message
# 600为keepalive的时间间隔
client.connect(mqtt_server, 1883, 600)
client.subscribe('surveillance', qos=0)
client.subscribe(r"$SYS/brokers/+/clients/#", qos=0)
# 启动mqtt消息订阅(非阻塞式)
client.loop_start()
以上代码运行后,若有其他客户端上线或下线,此处on_message()
回调方法,回收到系统上下线消息,如下所示:
客户端上线 topic: $SYS/brokers/[email protected]/clients/654321/connected
客户端上线消息报文 :
{
"username": "admin",
"ts": 1627476021893,
"sockport": 1883,
"proto_ver": 4,
"proto_name": "MQTT",
"keepalive": 600,
"ipaddress": "127.0.0.1",
"expiry_interval": 0,
"connected_at": 1627476021893,
"connack": 0,
"clientid": "654321",
"clean_start": true
}
客户端下线 topic: $SYS/brokers/[email protected]/clients/654321/disconnected
客户端下线消息报文 :
{
"username": "admin",
"ts": 1627476028659,
"reason": "tcp_closed",
"disconnected_at": 1627476028659,
"clientid": "654321"
}
可使用正则表达式r"^\$SYS\/brokers\/.*\/clients\/.*\/(dis)?connected"
匹配与过滤上下线消息以进行业务逻辑处理。