如何优雅退出WebSocket与asyncio事件循环示例?避免任务报错
解决WebSocket服务器优雅退出问题
我需要实现当buttcount达到3时,干净停止WebSocket服务器并退出程序,无错误信息。尝试用asyncio.get_event_loop().stop()时,出现大量未完成任务被销毁的报错。
原代码
#!/usr/bin/env python import asyncio import websockets buttcount=0 async def hello(websocket, path): global buttcount name = await websocket.recv() print(name) if name=="butt": buttcount += 1 if buttcount == 3: #Do something really cool to gracefully exit without error messages start_server = websockets.serve(hello, "localhost", 8765) asyncio.get_event_loop().run_until_complete(start_server) asyncio.get_event_loop().run_forever()
报错信息
Task was destroyed but it is pending! task: <Task pending name='Task-4' coro=<WebSocketServerProtocol.handler() running at /usr/local/lib/python3.8/dist-packages/websockets/legacy/server.py:244> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x7f74e6d08a60>()]>> Task was destroyed but it is pending! task: <Task pending name='Task-5' coro=<WebSocketCommonProtocol.transfer_data() running at /usr/local/lib/python3.8/dist-packages/websockets/legacy/protocol.py:968> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x7f74e6d08b20>()]> cb=[<TaskWakeupMethWrapper object at 0x7f74e6d08e80>()]>> Task was destroyed but it is pending! task: <Task pending name='Task-6' coro=<WebSocketCommonProtocol.keepalive_ping() running at /usr/local/lib/python3.8/dist-packages/websockets/legacy/protocol.py:1259> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x7f74e6d08eb0>()]>> Task was destroyed but it is pending! task: <Task pending name='Task-7' coro=<WebSocketCommonProtocol.close_connection() running at /usr/local/lib/python3.8/dist-packages/websockets/legacy/protocol.py:1314> wait_for=<Task pending name='Task-5' coro=<WebSocketCommonProtocol.transfer_data() running at /usr/local/lib/python3.8/dist-packages/websockets/legacy/protocol.py:968> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x7f74e6d08b20>()]> cb=[<TaskWakeupMethWrapper object at 0x7f74e6d08e80>()]>> Task was destroyed but it is pending! task: <Task pending name='Task-8' coro=<WebSocketCommonProtocol.write_close_frame() running at /usr/local/lib/python3.8/dist-packages/websockets/legacy/protocol.py:1216> cb=[_release_waiter(<Future pendi...4e6d08a60>()>)() at /usr/lib/python3.8/asyncio/tasks.py:429]> sys:1: RuntimeWarning: coroutine 'WebSocketCommonProtocol.write_close_frame' was never awaited Exception ignored in: <coroutine object WebSocketCommonProtocol.close_connection at 0x7f74e6ce6740> Traceback (most recent call last): File "/usr/local/lib/python3.8/dist-packages/websockets/legacy/protocol.py", line 1354, in close_connection File "/usr/local/lib/python3.8/dist-packages/websockets/legacy/protocol.py", line 1372, in close_transport File "/usr/local/lib/python3.8/dist-packages/websockets/legacy/protocol.py", line 1397, in wait_for_connection_lost File "/usr/lib/python3.8/asyncio/tasks.py", line 448, in wait_for
解决方案
要优雅退出,需要先关闭服务器停止接受新连接,等待现有连接处理完成,再停止事件循环。修改后的代码如下:
#!/usr/bin/env python import asyncio import websockets buttcount = 0 server = None # 保存服务器实例 async def hello(websocket, path): global buttcount, server name = await websocket.recv() print(name) if name == "butt": buttcount += 1 if buttcount == 3: # 关闭服务器,停止接受新连接 server.close() # 等待服务器完全关闭 await server.wait_closed() # 获取事件循环并停止 loop = asyncio.get_running_loop() loop.stop() async def main(): global server server = await websockets.serve(hello, "localhost", 8765) await server.wait_closed() # 等待服务器关闭信号 if __name__ == "__main__": loop = asyncio.get_event_loop() loop.run_until_complete(main()) # 清理剩余的任务 pending = asyncio.all_tasks(loop) loop.run_until_complete(asyncio.gather(*pending)) loop.close()
关键修改点
- 保存
websockets.serve()返回的服务器实例,用于后续关闭操作 - 当
buttcount达到3时,先调用server.close()停止接受新连接 - 用
await server.wait_closed()等待服务器完成关闭流程 - 使用
asyncio.get_running_loop().stop()停止事件循环 - 主函数中先运行服务器,等待关闭后再清理所有未完成任务,最后关闭事件循环
这样能确保所有WebSocket相关任务都正常完成,不会出现未完成任务被销毁的报错。
内容的提问来源于stack exchange,提问作者zenner55
相关产品推荐
相关产品推荐

