我正在创建一个python3龙卷风Web服务器,它可以侦听MQTT代理,并且每当侦听来自它的新消息时,都会通过Web套接字将其广播到所连接的浏览器。但是,似乎Tornado不喜欢从不同于IOLoop.current()的线程对其API进行调用,而且我也找不到其他解决方案...
我已经尝试编写一些代码。我将整个MQTT客户端(在本例中称为PMCU客户端)放在一个单独的线程上,该线程循环并监听MQTT通知。
def on_pmcu_data(data):
for websocket_client in websocket_clients:
print("Sending websocket message")
websocket_client.write_message(data) # Here it stuck!
print("Sent")
class WebSocketHandler(tornado.websocket.WebSocketHandler):
def open(self):
websocket_clients.append(self)
def on_close(self):
websocket_clients.remove(self)
def make_app():
return tornado.web.Application([
(r'/ws', WebSocketHandler)
])
if __name__ == "__main__":
main_loop = IOLoop().current()
pmcu_client = PMCUClient(on_pmcu_data)
threading.Thread(target=lambda: pmcu_client.listen("5.4.3.2")).start()
app = make_app()
app.listen(8080)
main_loop.start()
但是,正如我所说,似乎在IOLoop.current()块之外调用了Tornado API:上面的代码仅显示Sending websocket message
。
我的意图是在websocket_client.write_message(data)
事件循环上运行IOLoop.current()
。但是似乎IOLoop.current().spawn_callback(lambda: websocket_client.write_message(data))
启动后,功能IOLoop.current()
不起作用。我该如何实现?
我知道我对IOLoop,它所依赖的asyncio和python3异步有很大的误解。
答案 0 :(得分:0)
on_pmcu_data
在单独的线程中被调用,但是websocket由Tornado的事件循环控制。除非您有权访问事件循环,否则无法从线程写入Websocket。
您需要要求IOLoop
将数据写入websocket。
解决方案1:
在简单的情况下,如果您不想在代码中进行太多更改,可以执行以下操作:
if __name__ == "__main__":
main_loop = IOLoop().current()
on_pmcu_data_callback = lambda data: main_loop.add_callback(on_pmcu_data, data)
pmcu_client = PMCUClient(on_pmcu_data_callback)
...
这应该可以解决您的问题。
解决方案2:
对于更复杂的情况,可以将main_loop
传递给PMCUClient
类,然后使用add_callback
(或spawn_callback
)运行on_pmcu_data
。
示例:
if __name__ == "__main__":
main_loop = IOLoop().current()
pmcu_client = PMCUClient(on_pmcu_data, main_loop) # also pass the main loop
...
然后在PMCUCLient
类中:
class PMCUClient:
def __init__(self, on_pmcu_data, main_loop):
...
self.main_loop = main_loop
def lister(...):
...
self.main_loop.add_callback(self.on_pmcu_data, data)