温馨提示:这个包本人在实际使用中会遇到莫名其妙的bug,而且难以解决,如有其他选择建议还是不要使用HBMQTT了。
HBMQTT
简介
开源的Mqtt客户端和broker,使用python标准异步库asyncio
实现,提供协程并发程序开发的直接API。
- GitHub:https://github.com/beerfactory/hbmqtt
- 文档:https://hbmqtt.readthedocs.io/en/latest/
Feature
HBMQTT实现了完整的MQTT 3.1.1协议规范,并提供了以下特性:
- Support QoS 0, QoS 1 and QoS 2 messages flow
- 客户端断线自动重连
- 通过密钥文件认证(其他方法可通过插件系统实现)
- 基础的
$SYS
topics 支持 - TCP 和 websocket 支持
- TCP 和 websocket 的 SSL 支持
- 插件系统
依赖
要求Python >=3.4.3
安装
pip install hbmqtt==0.9.6
客户端API用法
MQTTClient
类实现了MQTT协议,用于订阅、发布消息,通过普通TCP或者websocket协议,也可选择安全或者不安全的方式。
1. 订阅并接收
订阅并且打印每条从broker接收到的消息
import asyncio
import logging
from hbmqtt.client import MQTTClient, ClientException
from hbmqtt.mqtt.constants import QOS_1, QOS_2
logger = logging.getLogger('test_sub')
my_config = {
'keep_alive': 10,
'ping_delay': 1,
'default_qos': 0,
'default_retain': False,
'auto_reconnect': True, # 开启自动重连
'reconnect_max_interval': 90, # 最大重试间隔 秒
'reconnect_retries': -1, # 一直重试,直到连上
}
async def uptime_coro():
# 创建客户端实例
C = MQTTClient(client_id='test1', config=my_config, loop=None)
await C.connect('mqtt://test.mosquitto.org/',
cleansession=True,
cafile=None,
extra_headers={})
# 订阅 $SYS/broker/uptime' QOS=1
# 订阅 $SYS/broker/load/#' QOS=2
await C.subscribe([
('$SYS/broker/uptime', QOS_1),
('$SYS/broker/load/#', QOS_2),
])
try:
# 接收100条消息
for i in range(0, 100):
# 等待接收消息, 超时时间为None表示一直等待,返回message对象
message = await C.deliver_message(timeout=None)
packet = message.publish_packet
print("%d: %s => %s" % (i, packet.variable_header.topic_name,
str(packet.payload.data)))
# 取消订阅
await C.unsubscribe(['$SYS/broker/uptime', '$SYS/broker/load/#'])
# 客户端主动断开连接
await C.disconnect()
except ClientException as ce:
logger.error("Client exception: %s" % ce)
if __name__ == '__main__':
formatter = "[%(asctime)s] %(name)s {%(filename)s:%(lineno)d} %(levelname)s - %(message)s"
logging.basicConfig(level=logging.DEBUG, format=formatter)
asyncio.get_event_loop().run_until_complete(uptime_coro())
2. 发布消息
异步发布3条消息,每条消息的服务质量都不同。
import logging
import asyncio
from hbmqtt.client import MQTTClient, ConnectException
from hbmqtt.mqtt.constants import QOS_0, QOS_1, QOS_2
logger = logging.getLogger('test_pub')
async def test_coro():
C = MQTTClient()
await C.connect('mqtt://test.mosquitto.org/')
tasks = [
asyncio.ensure_future(C.publish('a/b', b'TEST MESSAGE WITH QOS_0')),
asyncio.ensure_future(C.publish('a/b', b'TEST MESSAGE WITH QOS_1', qos=QOS_1)),
asyncio.ensure_future(C.publish('a/b', b'TEST MESSAGE WITH QOS_2', qos=QOS_2)),
]
await asyncio.wait(tasks)
logger.info("test_coro messages published")
await C.disconnect()
async def test_coro2():
try:
C = MQTTClient()
ret = await C.connect('mqtt://test.mosquitto.org:1883/')
message = await C.publish('a/b', b'TEST MESSAGE WITH QOS_0', qos=QOS_0)
message = await C.publish('a/b', b'TEST MESSAGE WITH QOS_1', qos=QOS_1)
message = await C.publish('a/b', b'TEST MESSAGE WITH QOS_2', qos=QOS_2)
# print(message)
logger.info("test_coro2 messages published")
await C.disconnect()
except ConnectException as ce:
logger.error("Connection failed: %s" % ce)
asyncio.get_event_loop().stop()
if __name__ == '__main__':
formatter = "[%(asctime)s] %(name)s {%(filename)s:%(lineno)d} %(levelname)s - %(message)s"
logging.basicConfig(level=logging.DEBUG, format=formatter)
asyncio.get_event_loop().run_until_complete(test_coro())
asyncio.get_event_loop().run_until_complete(test_coro2())
实际上这个脚本执行发布代码通过异步事件循环,先执行完 test_coro()
函数,后执行 test_coro2()
函数。两者完成的同样的任务。test_coro2()
是按先后顺序发送了3条,而test_coro()
是异步发送了3条。通过日志可发现发送的MQTT消息顺序的区别。
详细日志信息请自己运行查看。
test_coro()
异步发布消息的效率更高一些,(官方文档错了,还是我错了?😱)
3. api参数说明
1. 构造参数 [source]
用于创建客户端实例。
class hbmqtt.client.MQTTClient(client_id=None, config=None, loop=None)
参数:
-
client_id
: 客户端id,如果是None,则通过hbmqtt.utils.gen_client_id()
生成随机id -
config
: 客户端配置参数,python字典类型,默认为:{ 'keep_alive': 10, 'ping_delay': 1, 'default_qos': 0, 'default_retain': False, 'auto_reconnect': True, 'reconnect_max_interval': 10, 'reconnect_retries': 2, }
详细配置说明,请看下文
-
loop
:asynio
模块的loop
对象
2. MQTTClient.connect
该方法为协程,用于连接到broker
client.connect(uri=None, cleansession=None, cafile=None, capath=None, cadata=None, extra_headers={})
参数:
uri
:Broker 地址,格式见上一篇介绍cleansession
:是否清除原有会话cafile
: 服务证书文件(可选,用于安全连接)capath
: 服务证书路径(可选,用于安全连接)cadata
: 服务证书数据(可选,用于安全连接)extra_headers
:用于websocket初始化连接附加的http请求头
返回值:
CONNACK 消息返回值
异常:
连接失败抛出hbmqtt.client.ConnectException
3. MQTTClient.disconnect
协程,断开连接
client.disconnect()
4. MQTTClient.reconnect
协程,重连功能。重试次数和间隔可以通过reconnect_max_interval
和 reconnect_retries
参数控制
client.reconnect(cleansession=None)
参数
cleansession
:是否清除以前会话
返回值:
CONNACK 消息返回值
异常:
连接失败抛出hbmqtt.client.ConnectException
5. MQTTClient.ping
协程,发送 ping 消息
client.ping()
6. MQTTClient.publish
协程,发布消息。并根据服务质量等待确认消息
client.publish(topic, message, qos=None, retain=None)
参数:
topic
: 主题message
:消息载荷,bytes类型qos
:服务质量,取值0, 1, 2. 默认值根据连接配置中的default_qos
决定retain
:retail 标志位,默认值根据连接配置中的default_retain
决定
7. MQTTClient.subscribe
协程,订阅topic,发送订阅消息,并等待broker的确认包。
client.subscribe(topics)
参数:
topics
: 主题和QoS 的元组 组成的列表,如
[
('$SYS/broker/uptime', QOS_1),
('$SYS/broker/load/#', QOS_2)
]
返回:
SUBACK 确认消息返回值
8. MQTTClient.unsubscribe
协程,取消订阅。
client.unsubscribe(topics)
参数:
- topics:主题列表,如:
['$SYS/broker/uptime', '$SYS/broker/load/#']
9. MQTTClient.deliver_message
协程,等待从broker 接收下一条消息,如果没有收到消息,则会一直等待,或者指定等待的超时时间 timeout
。
client.deliver_message(timeout=None)
参数:
- timeout: 等待消息到达时间,单位:秒。
None
:一直等待
返回:
hbmqtt.session.ApplicationMessage
实例,包含消息的所有属性。
异常:
如果timeout参数超时,抛出 asyncio.TimeoutError
。
4. 连接配置参数
MQTTClient.__init__
接收一个 config
参数,python的字典类型,包含以下字段:
keep_alive
:keep alive时间,单位秒(默认10秒),客户端会自动发送 ping消息给broker,以维持客户端不掉线。ping_delay
在keep alive 超时之前自动发送 ping 消息的延迟,默认 1秒default_qos
:默认消息质量,在publish() 方法未指定qos参数时起作用。default_retain
:默认 retain值(False),在publish() 方法未指定qos参数时起作用。auto_reconnect
:是否自动重连,默认True
reconnect_max_interval
: 两次重连之间的最大间隔,默认为10sreconnect_retries
:最大重试次数,默认2次
默认QoS等级和retain值,可以被额外的topic
参数重写,例如
config = {
'keep_alive': 10,
'ping_delay': 1,
'default_qos': 0,
'default_retain': False,
'auto_reconnect': True,
'reconnect_max_interval': 5,
'reconnect_retries': 10,
'topics': {
'/test': { 'qos': 1 },
'/some_topic': { 'qos': 2, 'retain': True }
}
}
以上配置会设置所有消的默认QoS为0,retain值为False,除了:
- 发送到topic 为
/test
的消息,QoS默认值为1 - 发送到topic 为
/some_topic
的消息,QoS默认值为2, retain值为True