启动后在Tornado主循环上运行操作

时间:2019-04-26 16:23:35

标签: python-3.x tornado ioloop

我正在创建一个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异步有很大的误解。

1 个答案:

答案 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)