簡體   English   中英

Paho MQTT while loop 阻止發布到另一個 MQTT 客戶端

[英]Paho MQTT while loop is blocking publish to another MQTT client

我的目標是在 IoT 的一些模塊之間正確處理后端的 MQTT 消息。 我決定實現模塊模擬器 class,它將接收我的請求或發送響應。

第二個問題是,我需要在發布后等待模塊 ACK 或 ERR。 對於這個問題,我制作了這樣的 ack_blocker 列表:

[
    {
        "module_mac": "mac",
        "blocked": False,
        "message": {}
    },
    {
        "module_mac": "mac",
        "blocked": False,
        "message": {}
    }
]

因此,當我向特定模塊發送消息時,blocked 屬性將設置為 True,並且在發布消息后我將在 while 循環中等待。 另一方面,我發布的消息應該到達我的模擬器 MQTT 客戶端,在那里它將解析數據並響應 ERR 或 ACK。 在收到消息返回時,我會將阻塞屬性設置回 False 並且循環將結束並將消息返回到后端視圖,並帶有錯誤或正確的消息。

問題是,從后端發布的消息永遠不會到達模擬器 MQTT 客戶端。 IDK 為什么,但在我的循環中是超時(10 秒),在此之后我應該拋出模塊沒有響應的錯誤。 我非常仔細地調試整個過程,當后端要拋出錯誤時,我的模擬器客戶端最終會收到消息。 我跑了更多次,每次都會發生這種情況。 因此,我認為該循環以某種方式阻止了該消息的發送。

這是我的循環實現:

    def send_message(self, mac: str, message: str):
        self.publish(mac, message)
        end_time = time.time() + self.timeout

        while True:
            module_ack_blocker = next(filter(lambda obj: obj.get('module_mac') == mac, self.ack_blocker), None)

            if not module_ack_blocker.get('blocked'):
                response = module_ack_blocker.get('message')

                if response.get('result') == 'OK':
                    logging.getLogger('root_logger').info(f'[MQTT]: ACK Message received.')
                    return response
                elif response.get('result') == 'ERROR':
                    raise MQTTException(response.get('details'), status_code=mqtt_status.MQTT_ERR_NOT_SUPPORTED)

            if time.time() > end_time:
                raise MQTTException('Module is not responding.', status_code=mqtt_status.MQTT_ERR_UNKNOWN)

如您所見,首先我發布消息。 之后,我將計算超時並開始循環。 在循環中,我首先在 ack 阻止程序列表中查看正確的 dict(就像我之前提到的那樣)。 我會問它是否沒有被阻止。 在那之后,如果我還有時間超時。

我的 mqtt 模擬器客戶端如下所示:

class MqttClientEmulator(object):
    def __init__(self):
        self.app = None
        self.broker_host = None
        self.broker_port = None
        self.keep_alive = None
        self.timeout = None

        self.client = mqtt.Client(client_id='brewmaster_client_emulator')

    def init(self, app):
        self.broker_host = os.getenv('BROKER_HOST')
        self.broker_port = int(os.getenv('BROKER_PORT'))
        self.keep_alive = int(os.getenv('MQTT_KEEPALIVE'))
        self.timeout = int(os.getenv('MQTT_TIMEOUT'))
        self.app = app

        self.client.on_message = self.on_message

    def on_message(self, client, userdata, msg):
        topic = msg.topic
        string_message = str(msg.payload.decode('utf-8'))
        dict_message = json.loads(string_message)

        # Request result
        if dict_message.get('device_uuid'):
            print(dict_message)
            response = {
                "module_mac": topic,
                "sequence_number": 123,
                "result": "OK",
                "details": ""
            }
            time.sleep(1)   # Just for time reserve (this code will be more complicated in future)
            self.publish('brewmaster-backend', json.dumps(response))

    def connect(self):
        self.client.connect(self.broker_host, self.broker_port, self.keep_alive)
        self.client.loop_start()

    def disconnect(self):
        self.client.loop_stop()
        self.client.disconnect()

    def subscribe(self, name):
        self.client.subscribe(name)

    def publish(self, topic, message):
        self.client.publish(topic, message)

我也嘗試了線程,它也沒有效果。

好的,我需要更深入地了解 paho MQTT 庫。 object MQTTMessageInfo 有 function wait_for_publish。 如果你看一下_condition object,它已經用信號量實現了鎖。 所以我需要做的就是將我的 MQTT 客戶端方法 send_message(如問題所示)中的 While 循環更改為如下所示:

    def send_message(self, mac: str, message: str):
        result = self.publish(mac, message)
        end_time = time.time() + self.timeout

        if result.rc == mqtt.MQTT_ERR_QUEUE_SIZE:
            raise ValueError('Message is not queued due to ERR_QUEUE_SIZE')
        with result._condition:
            while True:
                module_ack_blocker = next(filter(lambda obj: obj.get('module_mac') == mac, self.ack_blocker), None)

                if not module_ack_blocker.get('blocked'):
                    response = module_ack_blocker.get('message')

                    if response.get('result') == 'OK':
                        logging.getLogger('root_logger').info(f'[MQTT]: ACK Message received.')
                        return response
                    elif response.get('result') == 'ERROR':
                        raise MQTTException(response.get('details'), status_code=mqtt_status.MQTT_ERR_NOT_SUPPORTED)

                if time.time() > end_time:
                    raise MQTTException('Daný modul neodpovedá.', status_code=mqtt_status.MQTT_ERR_UNKNOWN)
                result._condition.wait(1)

結果是 MQTTMessageInfo object 並且 _condition.wait(1) 正在等待超時 1 秒。 所以基本上什么時候等待所有其他進程都在工作,1秒后將開始另一個while循環迭代並檢查,如果消息已經到達。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM