如何关闭承载同步HTTPServer的异步任务?解决活跃连接阻塞退出问题
问题解决:带活跃连接时Python HTTP服务器无法按时退出
问题根源
当存在活跃连接(如netcat保持的长连接)时,TCPServer.shutdown()仅会停止服务器接受新连接,但会等待当前活跃连接的请求处理完成后才会退出serve_forever()循环。如果连接一直保持,服务器就会卡住,无法响应关闭指令。
解决方案
自定义服务器类,跟踪所有活跃的客户端连接,在关闭时主动断开这些连接,强制终止正在处理的请求,让serve_forever()快速退出。
修改后的完整代码
import asyncio import http.server import socketserver import socket class StoppableHTTPServer(socketserver.TCPServer): allow_reuse_address = True def __init__(self, server_address, RequestHandlerClass): super().__init__(server_address, RequestHandlerClass) self.active_connections = set() def process_request(self, request, client_address): # 记录当前活跃的客户端连接 self.active_connections.add(request) try: super().process_request(request, client_address) finally: # 请求处理完毕后移除记录 self.active_connections.discard(request) def stop(self): # 停止接受新连接 self.shutdown() # 主动关闭所有活跃客户端连接 for conn in self.active_connections: try: conn.shutdown(socket.SHUT_RDWR) conn.close() except Exception: # 忽略关闭时的异常(如连接已断开) pass # 关闭服务器自身的监听套接字 self.server_close() Handler = http.server.SimpleHTTPRequestHandler def blocking_function(server): print("This is a blocking function.") with server: print("Serving on port 80") # 缩短轮询间隔,加快shutdown指令的检测速度 server.serve_forever(poll_interval=0.5) async def main(): server = StoppableHTTPServer(("", 80), Handler) task = asyncio.create_task(asyncio.to_thread(blocking_function, server)) await asyncio.sleep(3) task.cancel() try: await task except asyncio.CancelledError: print("Task was cancelled.") server.stop() print("Back to event loop.") asyncio.run(main())
关键修改说明
- 跟踪活跃连接:新增
active_connections集合,通过重写process_request方法记录所有正在处理的客户端连接套接字。 - 强制断开连接:在
stop方法中,先调用shutdown()停止接受新连接,再遍历关闭所有活跃客户端连接,打断正在阻塞的请求处理流程。 - 加快退出响应:给
serve_forever()设置poll_interval=0.5,让服务器更频繁地检查是否收到shutdown请求,缩短退出延迟。
内容的提问来源于stack exchange,提问作者LuckyCoder3607
相关产品推荐
相关产品推荐

