[英]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.