如何在Python 3.8中优雅停止asyncio服务器
如何在Python 3.8中优雅停止asyncio服务器
我之前在Python3.8环境下也碰到过一模一样的问题,直接调用loop.stop()确实会导致那个讨厌的RuntimeError——因为asyncio.run()期望整个异步流程正常结束,而不是被强制终止事件循环。下面是亲测有效的解决方案:
问题根源
你当前代码里的asyncio.get_event_loop().stop()会直接终止事件循环,但此时server.serve_forever()这个异步任务还没完成,asyncio.run()会一直等待main()函数执行完毕,事件循环提前停止就触发了那个报错。
解决方案:优雅关闭服务器实例
核心思路是不要硬停事件循环,而是通过关闭服务器实例让整个异步流程自然收尾,具体修改步骤如下:
- 在
Server类中新增属性保存服务器实例,这样在处理客户端的方法里能访问到它:
class Server: def __init__(self, host, port): self.host = host self.port = port self.server = None # 新增:用于保存服务器实例
- 在
start()方法中,将创建的服务器实例赋值给self.server:
async def start(self): self.server = await asyncio.start_server(self.handle_client, self.host, self.port) async with self.server: print(f"Serving on {self.host}:{self.port}") await self.server.serve_forever()
- 修改
handle_client中的停止逻辑,替换掉asyncio.get_event_loop().stop(),改为优雅关闭服务器:
# 替换原来的loop.stop()代码 if self.server: await self.server.close() await self.server.wait_closed()
另外还要注意一个细节:客户端发送的quit可能带有换行符,所以在读取请求时最好用strip()处理一下,避免因为多余的空白字符导致判断失效。
修改后的完整服务器代码
import socket import asyncio class Server: def __init__(self, host, port): self.host = host self.port = port self.server = None # 保存服务器实例 async def handle_client(self, reader, writer): addr = writer.get_extra_info('peername') print(f'Accepted connection from {addr!r}') request = None try: while request != 'quit': # 用strip()去除可能的换行/空格,避免判断失效 request = (await reader.read(255)).decode('utf8').strip() print(f"Received {request!r} from {addr!r}") # 针对quit请求返回特定响应 if request == 'quit': response = 'server shutting down' else: response = 'ok' writer.write(response.encode('utf8')) await writer.drain() print(f'Sent {response!r} to {addr!r}') print('----------') except Exception as e: print(f"Error handling client {addr!r}: {e}") finally: print(f"Connection closed by {addr!r}") # 关闭客户端连接 writer.close() await writer.wait_closed() print(f'Closed the connection with {addr!r}') # 优雅关闭服务器 if self.server: await self.server.close() await self.server.wait_closed() async def start(self): self.server = await asyncio.start_server(self.handle_client, self.host, self.port) async with self.server: print(f"Serving on {self.host}:{self.port}") await self.server.serve_forever() async def main(): server = Server(socket.gethostname(), 5000) await server.start() if __name__ == '__main__': asyncio.run(main())
为什么这样能正常工作?
当收到quit请求后,我们先给客户端返回响应,关闭客户端连接,然后调用server.close()停止接受新连接,server.wait_closed()等待服务器资源清理完成。之后server.serve_forever()会自动退出,async with块会完成服务器的上下文清理,start()方法正常结束,main()函数执行完毕,asyncio.run()就会优雅退出,不会再抛出错误。
备注:内容来源于stack exchange,提问作者Ed Morton
相关产品推荐
相关产品推荐

