You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义ASGI中间件引发lifespan协议不兼容及K8s部署异常

自定义中间件导致ASGI Lifespan协议不兼容问题的分析与修复

问题场景

为处理客户端未等待响应就断开的场景,编写了如下自定义中间件NoResponseReturnMiddleware:

class NoResponseReturnMiddleware(BaseHTTPMiddleware):
    _ERROR_MESSAGE: str = "No response returned."

    def __init__(self, app: ASGIApp, logger: Logger):
        super().__init__(app)
        self.logger = logger

    async def __call__(self, scope, receive, send) -> None:
        request = Request(scope, receive, send)
        try:
            await super().__call__(scope, receive, send)
        except RuntimeError as exc:
            if await request.is_disconnected() and str(exc) == self._ERROR_MESSAGE:
                self.logger.debug(
                    "Remote client disconnected",
                    extra={
                        "url": request.url,
                        "method": request.method,
                        "path": request.url.path,
                        "query": request.url.query,
                    },
                )
            else:
                raise exc

    async def dispatch(self, request, call_next):
        return await call_next(request)

启动应用时收到提示:

ASGI 'lifespan' protocol appears unsupported

本地运行正常,但部署到Kubernetes后,Pod进入永久重启状态,Kubelet报错:

Warning Unhealthy 5m5s (x3 over 5m25s) kubelet Liveness probe failed: Get "http://11.11.11.11.1:8000/": dial tcp 11.11.11.11.1:8000: connect: connection refused

进一步测试发现,执行命令uvicorn main:app --reload --lifespan on时触发断言错误:

assert scope["type"] in ("http", "websocket") AssertionError

问题根源

自定义中间件的__call__方法未处理ASGI协议中的lifespan类型请求。当启用lifespan协议时,Uvicorn会发送type为lifespan的scope,而代码中直接创建Request对象,Request内部会断言scope类型必须是http或websocket,导致断言失败,服务启动失败,进而Kubernetes存活探针无法连接,触发Pod重启。

修复方案

修改__call__方法,仅在scope类型为http或websocket时执行自定义逻辑,其他类型(如lifespan)直接调用父类方法处理:

async def __call__(self, scope, receive, send) -> None:
    if scope["type"] in ["http", "websocket"]:
        request = Request(scope, receive, send)
        try:
            await super().__call__(scope, receive, send)
        except RuntimeError as exc:
            if await request.is_disconnected() and str(exc) == self._ERROR_MESSAGE:
                self.logger.debug(
                    "Remote client disconnected",
                    extra={
                        "url": request.url,
                        "method": request.method,
                        "path": request.url.path,
                        "query": request.url.query,
                    },
                )
            else:
                raise exc
    else:
        # 处理lifespan等其他ASGI协议类型
        await super().__call__(scope, receive, send)

这样可以兼容ASGI的所有协议类型,解决lifespan协议不支持的问题,避免服务启动失败导致的Pod重启。

内容的提问来源于stack exchange,提问作者SmokyICE

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 12:39:52