业务需求是这样的,一个mqtt client 与mqtt 服务器建立连接后,client.on_message 接收其他client 发来的数据处理请求,针对每个请求建立新的线程负责数据处理和处理结果返回(推送,client.publish)。
针对以上需求,基于java 有专门的MqttAsyncClient 类来满足,代码连接如下:
(1条消息)Eclipse paho 实现的基于MqttAsyncClient类的 异步Java 客户端_Jerry_liu20080504的专栏-CSDN博客_jerry
但是基于python 没有相应的类进行处理。网上找的很多基于python 的关于paho.mqtt 的示例都是很简单的收发共用同一个client 实例。但是在大量推送消息并发业务需求下,尤其发送数据量大的情况下,这种会造成多条推送消息的线程等待,使得单client 成为系统瓶颈。为了解决这个问题,找了很多资料,但是没找到比较成形的解决办法。后来接触到了asyncio,基于python 的paho.mqtt 官方示例,网址如下:
paho.mqtt.python/loop_asyncio.py at master · eclipse/paho.mqtt.python · GitHub
https://github.com/eclipse/paho.mqtt.python/blob/master/examples/loop_asyncio.py
但是由于对asyncio 知识欠缺,想研究透彻需要一定的时间投入,时间有限,放弃了对它的研究。接着寻找办法。
找了两天,功夫不负有心人,后来接触了paho.mqtt.publish 发现可以一次推送消息,重要的是不与client.on_message 共用一个实例。基本解决收发不可同时的问题。
代码如下:
# encoding=utf8
import paho.mqtt.client as mqtt
import paho.mqtt.publish as publish
import time
import socket
import requests
import threading
from threading import Thread
# 基于python 语言的paho.mqtt 多线程异步示例
# 2020/07/20
def get_host_ip():
"""
查询本机ip地址
:return:
"""
try:
s=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
s.connect(('8.8.8.8',80))
ip=s.getsockname()[0]
finally:
s.close()
return ip
# The callback for when the client receives a CONNACK response from the server.
def on_connect(client, userdata, flags, rc):
print("Connected with result code "+str(rc))
if 0==rc:
print("连接成功")
elif 1==rc:
print("连接失败-不正确的协议版本")
elif 2==rc:
print("连接失败-无效的客户端标识符")
elif 3==rc:
print("连接失败-服务器不可用")
elif 4==rc:
print("连接失败-错误的用户名或密码")
elif 5==rc:
print("连接失败-未授权")
else:
print("6-255: 未定义.")
# Subscribing in on_connect() means that if we lose the connection and
# reconnect then subscriptions will be renewed.
# client.subscribe("$SYS/#")
print(get_host_ip()) # 获取本机的内网ip
print(requests.get('http://ifconfig.me/ip', timeout=1).text.strip()) #获取本机的外网ip
client.subscribe("demo")# 订阅主题为 /geometricCorrection/本机ip
# 带thr 开头的函数都是线程函数
def thr_send_message():
print("发送donehello 消息。。。")
# print(client)
print("子线程id:" + str(threading.current_thread().ident))
time.sleep(5)
# client.publish("donehello", "donehello") #这种方式下的client.on_message 和 client.publish 共用唯一一个client 实例,会造成无法同时收发问题。
publish.single("donehello", "donehello", hostname="xx.xx.xx.xx",port=1883) # 通过该方式使client.on_message 和 client.publish 不再共用同一个client,解决了收发无法同时的问题。
print("发送donehello 消息完毕!")
# The callback for when a PUBLISH message is received from the server.
def on_message(client, userdata, msg):
# print(client)
print(msg.topic+" "+str(msg.payload))
thread_send_message = Thread(target=thr_send_message) # 建子线程处理新来的任务请求
thread_send_message.start()
time.sleep(10)
# print('结束时间:', time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))
client.publish("done", "done")
client = mqtt.Client("feizusemqtt"+ get_host_ip())
client.on_connect = on_connect
client.on_message = on_message
client.connect("XX.XX.XX.XX", 1883, 60)
# Blocking call that processes network traffic, dispatches callbacks and
# handles reconnecting.
# Other loop*() functions are available that give a threaded interface and a
# manual interface.
client.loop_forever()
# loop_forever()之后的语句不会执行