在我们的代码中,我们创建了一个使用aiohttp-web捕获webhook的应用程序。Application((通过循环.create_server((方法与服务器连接。
有时(读取-很难再现,但经常足够(,即使在代码终止后,侦听服务器也会继续运行。
例如:
> sudo lsof -i -P -n | grep LISTEN
systemd-r 463 systemd-resolve 13u IPv4 2459 0t0 TCP 127.0.0.53:53 (LISTEN)
sshd 689 root 3u IPv4 22779 0t0 TCP *:22 (LISTEN)
sshd 689 root 4u IPv6 22781 0t0 TCP *:22 (LISTEN)
python3 2728 ubuntu 7u IPv4 334240 0t0 TCP *:8991 (LISTEN)
...
python3 2808 ubuntu 7u IPv4 351159 0t0 TCP *:8991 (LISTEN)
这导致了当幽灵服务器(或在某些糟糕的情况下的服务器(吃掉了为生者准备的请求时会造成混乱:(
我们运行纯异步-没有线程-没有多处理。有人知道这是怎么发生的,以及如何防止这种情况发生吗我是否可以使用某种上下文管理器或方法,在程序完成后杀死Web服务器
编辑:
- 当程序到达代码末尾并正常退出时(正在调用shutdown(,就会发生这种情况
裸代码示例。。。
import asyncio
from aiohttp import web
WEBHOOK = "/listen"
class WebServer():
def __init__(self):
self.app = web.Application()
self.app.router.add_post(WEBHOOK, self.webhook_streams)
self.server = None
async def start(self, local_ip, local_port):
if self.server:
return
loop = asyncio.get_event_loop()
self.server = await loop.create_server(
self.app.make_handler(),
local_ip,
local_port,
reuse_port=True, # This will allow to bind to already used port, otherwise it will simply fail if there is ghost server present
)
async def webhook_streams(self, raw):
pass
async def shutdown(self):
print("Shutting down..")
asyncio.get_event_loop().stop()
pending = asyncio.Task.all_tasks()
for task in pending:
task.cancel()
if __name__ == '__main__':
ws = WebServer()
await ws.start("0.0.0.0", 8000)
# ....some other stuff....
await shutdown()
关键是要等到服务器正确关闭所有连接。。。例如,这需要添加到Web服务器类中
async def stop(self):
"stops server"
if not self.server:
return True
self.server.close() # this must be synchronous which is confusing
await self.server.wait_closed() # here you actually wait for server to close
"stopped server...."
return not self.server.is_serving()