之前写过如何 在服务器重启的时候感知长连接,最近发现折腾复杂了。
https://github.com/encode/starlette/discussions/1776
测试代码:
import asyncio
async def async_streamer():
try:
while True:
yield b"--boundary\r\nContent-Type: text/plain\r\nContent-Length: 1\r\n\r\n1\r\n"
await asyncio.sleep(0)
except asyncio.CancelledError:
print("caught cancelled error")
app = Starlette(routes=[
Route('/async', async_endpoint),
])
跑起来: uvicorn stream:app
这段代码粗一看没啥大不了的,但是神奇的地方在于,如果去掉 try ... except asyncio.CancelledError
,代码也能正常跑
async def async_streamer():
while True:
yield b"--boundary\r\nContent-Type: text/plain\r\nContent-Length: 1\r\n\r\n1\r\n"
await asyncio.sleep(0)
而且不会报错!最蛋痛的是,这玩意因为是个 while True
,如果你里面有打开的数据库连接,是不会中断的,也不会回收的。因为这个 coroutine 没有继续 async for
来消费,就一直挂在进程里当僵尸了!
加上 try ... except asyncio.CancelledError
,能捕获出错,也能处理善后了。
真是神奇啊。不知道是 uvicorn 的特性,还是 ASGI 都这样。