业务需求是这样的,一个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()之后的语句不会执行