簡體   English   中英

Python Tornado 從另一個線程發送 WebSocket 消息

[英]Python Tornado send WebSocket messages from another thread

我想在 Python 中使用 WebSocket,以使 web 客戶端了解我使用 PySerial 從串行端口讀取的數據的最新信息。 我目前正在使用以下代碼通過單獨的線程連續讀取串行數據

def read_from_port():
    while running:
        reading = ser.readline().decode()
        handle_data(reading)

thread = threading.Thread(target=read_from_port)
thread.daemon = True
thread.start()

我正在對串行數據執行一些處理,然后如果計算結果與其先前的值不同,我想向所有連接的 WebSocket 客戶端廣播一條消息。 為此,我設置了以下代碼

clients = []

def Broadcast(message):
    for client in clients:
        client.sendMessage(json.dumps(message).encode('utf8'))
        print("broadcasted")

worker.broadcast = Broadcast

class WSHandler(tornado.websocket.WebSocketHandler):
    def open(self):
        print('new connection')
        clients.append(self)
      
    def on_message(self, message):
        print('message received:  %s' % message)
        response = handler.HandleRequest(message, self.write_message)
 
    def on_close(self):
        print('connection closed')
        clients.remove(self)
 
    def check_origin(self, origin):
        return True
 
application = tornado.web.Application([
    (r'/ws', WSHandler),
])
 
if __name__ == "__main__":
    http_server = tornado.httpserver.HTTPServer(application)
    http_server.listen(8765)
    myIP = socket.gethostbyname(socket.gethostname())
    print('*** Websocket Server Started at %s***' % myIP)
    tornado.ioloop.IOLoop.instance().start()

然后我想在工作人員中使用“廣播”方法來廣播結果。 在工作線程中使用此方法會產生以下錯誤

File "main.py", line 18, in Broadcast
    client.write_message(message)
  File "/usr/local/lib/python3.8/site-packages/tornado/websocket.py", line 342, in write_message
    return self.ws_connection.write_message(message, binary=binary)
  File "/usr/local/lib/python3.8/site-packages/tornado/websocket.py", line 1098, in write_message
    fut = self._write_frame(True, opcode, message, flags=flags)
  File "/usr/local/lib/python3.8/site-packages/tornado/websocket.py", line 1075, in _write_frame
    return self.stream.write(frame)
  File "/usr/local/lib/python3.8/site-packages/tornado/iostream.py", line 555, in write
    future = Future()  # type: Future[None]
  File "/usr/local/Cellar/python@3.8/3.8.3_1/Frameworks/Python.framework/Versions/3.8/lib/python3.8/asyncio/events.py", line 639, in get_event_loop
    raise RuntimeError('There is no current event loop in thread %r.'
RuntimeError: There is no current event loop in thread 'Thread-1'.

我了解問題是 Tornado write_message function 不是線程安全的,並且正在產生此錯誤,因為我試圖直接從工作線程調用 function。 據我所知,在 Tornado 中使用並發代碼的推薦方法是通過 asyncio,但我認為在這種情況下線程方法可能更合適,因為我有一個基本上持續並行運行的循環。

不幸的是,我對 asyncio 以及如何在 Python 中實現線程知之甚少,所以我想找出從不同線程發送 WebSocket 消息的最簡單方法是什么。

https://docs.python.org/3/library/asyncio-dev.html#asyncio-multithreading閱讀有關同時使用 asyncio 和多線程的官方文檔給了我必要的線索,即使用“call_soon_threadsafe”可以非常優雅地實現這一點" function。 因此,以下代碼似乎可以解決問題

tornado.ioloop.IOLoop.configure("tornado.platform.asyncio.AsyncIOLoop")
io_loop = tornado.ioloop.IOLoop.current()
asyncio.set_event_loop(io_loop.asyncio_loop)

clients = []

def bcint(message):
    for client in clients:
        client.write_message(message)
        print("broadcasted")

def Broadcast(message):
    io_loop.asyncio_loop.call_soon_threadsafe(bcint, message)

worker.broadcast = Broadcast

class WSHandler(tornado.websocket.WebSocketHandler):
    def open(self):
        print('new connection')
        clients.append(self)
      
    def on_message(self, message):
        print('message received:  %s' % message)
        response = handler.HandleRequest(message, self.write_message)
 
    def on_close(self):
        print('connection closed')
        clients.remove(self)
 
    def check_origin(self, origin):
        return True
 
application = tornado.web.Application([
    (r'/ws', WSHandler),
])
 
if __name__ == "__main__":
    http_server = tornado.httpserver.HTTPServer(application)
    http_server.listen(8765)
    myIP = socket.gethostbyname(socket.gethostname())
    print('*** Websocket Server Started at %s***' % myIP)
    tornado.ioloop.IOLoop.current().start()

一種更簡潔的選擇是使用隊列,例如pyzmq ,它可以幫助您建立從一個線程到另一個線程的通信。

查看您的用例,您可以使用 PUB/SUB model。 這是一個示例代碼 此外,您可以使用“inproc”而不是“tcp”。 這將減少延遲,因為您將在同一進程中的多個線程之間進行通信。

暫無
暫無

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

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