Python中MQTT
Python有许多优秀的MQTT客户端,比较有代表性的有paho-mqtt、hbmqtt、gmqtt等,各有特色
-
paho-mqtt 有着最优秀的文档,代码风格易于理解,同时有着强大的基金会支持,目前新版本支持 MQTT 5.0
-
hbmqtt 使用 asyncio 库实现,可以优化网络 I/O 带来的延迟,但是代码风格不友好,文档较少,不支持 MQTT 5.0,且不再维护,被原作者弃用,有一个分支amqtt正在由不同的人积极开发
-
gmqtt 同样通过 asyncio 库实现,相比 HBMQTT ,代码风格友好,最重要的是支持 MQTT 5.0
paho-mqtt 可以说是 Python MQTT 开源客户端库中的佼佼者, 支持5.0、3.1.1 和 3.1 的MQTT 协议,由 Eclipse 基金会主导开发.在基金会的支持下,以每年一个版本的速度更新,稳定、持续,本文使用新版本paho-mqtt v1.6.1
参考文章
paho-mqtt安装
使用pip安装
pip3 install paho-mqtt
# virtualenv安装和完整代码参考官方文档
paho-mqtt已知的一些限制
截止1.6.1版本,当 clean_session 为 False 时,session 只存储在内存中,不持久化。这意味着当客户端重新启动时(不仅仅是重新连接,通常是因为程序重新启动而重新创建对象)会话丢失。这可能导致消息丢失。
客户端会话的以下部分丢失:
已从服务器收到但尚未完全确认的 QoS 2 消息。
由于客户端将盲目地确认任何 PUBCOMP(QoS 2 事务的最后一条消息),它不会挂起但会丢失此 QoS 2 消息。
已发送到服务器但尚未完全确认的 QoS 1 和 QoS 2 消息。
这意味着传递给 publish() 的消息可能会丢失。这可以通过注意传递给 publish() 的所有消息都有相应的 on_publish() 调用来缓解。
这也意味着代理可能在会话中有 Qos2 消息。由于客户端从一个空会话开始,它不知道它并将重新使用中间。这还没有确定。
此外,当 clean_session 为 True 时,该库将在网络重新连接时重新发布 QoS > 0 消息。这意味着 QoS > 0 消息不会丢失。但是标准规定我们是否应该丢弃发送了发布数据包的任何消息。我们的选择意味着我们不符合标准,并且 QoS 2 有可能被接收两次。如果只需要一次交付的 QoS 2 保证,应该 clean_session = False
paho-mqtt使用
使用paho-mqtt实现客户端相关功能简单步骤如下:
- 构造Client客户端实例
- 使用connect相关方法将创建的客户端连接到代理
- 使用loop相关方法维护和broker的通信
- 使用subscribe()方法订阅主题、接收消息
- 使用publish()方法发送消息
- 使用disconnect()断开连接
Client客户端
使用客户端连接代理、订阅等,首先我们需要先创建一个客户端,paho-mqtt使用Client()创建客户端实例
Client类的构造参数
# Client 源码 参数如下
def __init__(self, client_id="", clean_session=None, userdata=None,
protocol=MQTTv311, transport="tcp", reconnect_on_failure=True):
Client类构造参数讲解
# 参数示意
client_id = '客户端ID,str类型,可自定义'
'''
如果长度为零或者为空,则会随机生成一个,这种情况下,clean_session参数必须为True清除会话,因为持久会话需要client_id为唯一标识来恢复会话
'''
clean_session = '会话清除状态,bool类型,设为True,broker在断开连接的时候删除该client的信息,为False,则相当于是持久会话'
'''
clean_session在官方文档示例默认值为True,查看1.6.1版本源码默认值为None,看逻辑应该是支持mqttv5之后做的的变化
源码中判断如果protocol是MQTT V5版本 clean_session不是None的话则抛出ValueError,就是mqtt5的clean_session必须持久会话,
如果不是mqtt5的话如果clean_session is None 则将clean_session设置为True,这块就与官方文档的默认逻辑对应上了
'''
userdata = 'client用户数据,传递给回调函数,可以是任意类型,可以使用Clinet的 user_data_set()函数进行更新数据'
protocol = '客户端协议的版本,默认是MQTTv311就是3.1.1版本,也可以是MQTTv31、MQTTv5版本'
'''
protocol的参数在源码中是以下对应关系,理论上直接传入对应int值或者导入MQTTv** 字段传入都可
MQTTv31 = 3
MQTTv311 = 4
MQTTv5 = 5
'''
transport = '底层传输协议,默认是使用原始tcp,值可以设置为websockets通过ws发送mqtt'
reconnect_on_failure = 'bool类型, 连接失败后是否自动重新connect,默认为True'
'''
官方文档没有reconnect_on_failure相关示例和解释,不确定老版本有没有该字段
'''
创建客户端
# -*- coding: utf-8 -*-
# @Time: 2023/5/10 16:09
# @Author: LiQi
# @Describe:
import paho.mqtt.client as mqtt # 导入clinet 别名 mqtt
# 创建一个客户端实例赋值client,client_id自定义,其他参数根据需要设定
client = mqtt.Client(client_id='muziqi')
重置客户端
'''paho-mqtt提供reinitialise方法重新初始化已经构造好的客户端'''
# 上面创建的客户端clinet,直接调用reinitialise
client.reinitialise()
'''
reinitialise方法拥有下面三个参数
client_id="", clean_session=True, userdata=None
'''
Clinet选项函数
选项函数的作用是给构造好的客户端添加附加选项,一般情况下在使用content连接broker前完成,比如设置Client的证书、回调、账号密码等,根据具体业务使用,设置完成后使用conten连接到broker
max_inflight_messages_set
当QoS> 0的时候,可以存在的部分完成(已经发送,还没有确认成功的消息)的消息的最大数量,这些消息可以同时通过网络流,默认为20.增加此值将消耗更多内存,但可以增加吞吐量
# inflight参数为要修改的数量,最小为1,小于1则抛出ValueError
client.max_inflight_messages_set(inflight)
max_queued_messages_set
设置传出消息队列中等待处理的QoS> 0的传出消息的最大数量,默认为0表示无限制,当队列已满时,其他传出的消息都将被丢弃,实际使用中0实际上并不是无限,目前实现最大可为65555,包括队列中的65535条消息+inflight的默认20条消息,如果inflight默认值被消息,队列中的实际消息数量被减少
# queue_size是要修改的最大数量
client.max_queued_messages_set(queue_size)
message_retry_set-废弃
QoS>0 的消息,如果发送之后超过一定时间broker没有响应,重发消息的时间,单位是s,默认5s
'''
源码注释该方法不再使用,在2.0版本删除
'''
ws_set_options
设置WebSocket的连接选项,构造Client时transport="websockets"才会使用,connect相关方法之前调用
import paho.mqtt.client as mqtt
# 传输协议设置为ws
client = mqtt.Client(client_id='muziqi',transport='websockets')
'''
path:broker的mqtt 路径,以/开头的字符串
headers:头部信息可以是字典或者是可调用对象。如果是字典的话字典中额外的项被添加到websocket头中。如果是一个可调用的对象,默认的websocket的头部信息被传递到这个函数,将函数结果当做新的头不新鲜'''
client.ws_set_options(path="/mqtt"