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

FastAPI SSE本地正常但Azure Web App部署后无法工作求助

问题描述

基于FastAPI搭建的SSE通知系统本地运行正常,但部署到Azure容器Web App后出现异常:服务器日志显示通知已发送并处理,但前端(Vue)无法接收。已尝试添加Cache-Control、X-Accel-Buffering请求头禁用响应缓冲,因采用容器部署未找到web.config等效配置。

路由示例代码:

@router.get(
    "/stream-notifs",
    response_description="User task notifications",
    response_model=Response,
    tags=["users"],
)
async def notif_update_stream(request: Request, user_id: str):

    MESSAGE_STREAM_DELAY = 1  # second
    MESSAGE_STREAM_RETRY_TIMEOUT = 15000  # milisecond

    client_host = request.client.host
    print(f"Client {client_host} connected.")

    async def event_generator():
        notif_q: asyncio.Queue = request.state.notif_q
        this_user_notifs = []

        while True:
            notif_data: NotificationData = await notif_q.get()

            if notif_data.creator_id == UUID(user_id):
                # TODO: Add validation
                this_user_notifs.append(notif_data)
                print(f"Sending to: {request.client}")
            else:
                notif_q.put_nowait(notif_data)

            # If client was closed the connection
            if await request.is_disconnected():
                break

            # Checks for new messages and return them to client if any
            for curr_notif in this_user_notifs:  # Whenever a task is completed
                notif = Notification(
                    id="message_id",
                    event="completed_task",
                    retry=MESSAGE_STREAM_RETRY_TIMEOUT,
                    data=curr_notif,
                ).model_dump_json()
                yield notif
                this_user_notifs.pop()
                notif_q.task_done()
                print(f"Finished notif {notif}")

            await asyncio.sleep(MESSAGE_STREAM_DELAY)

        await notif_q.join()

    return EventSourceResponse(
        event_generator(),
        media_type="text/event-stream",
        headers={
            "Cache-Control": "no-cache",
            "X-Accel-Buffering": "no",
        },
    )
解决建议
  • 开启Azure Web App的WebSocket支持
    登录Azure Portal,进入目标Web App的「配置」>「常规设置」,将WebSocket协议设置为「启用」。SSE依赖HTTP长连接,开启WebSocket后能让Azure代理层更好地兼容这类流式响应;同时确保「ARR Affinity」(应用请求路由亲和性)处于开启状态,保证同一客户端的请求始终路由到同一容器实例。

  • 补充长连接相关响应头
    在EventSourceResponse的headers中添加Connection: keep-alive和Transfer-Encoding: chunked,明确告知代理层这是分块传输的长连接:

    return EventSourceResponse(
        event_generator(),
        media_type="text/event-stream",
        headers={
            "Cache-Control": "no-cache",
            "X-Accel-Buffering": "no",
            "Connection": "keep-alive",
            "Transfer-Encoding": "chunked",
        },
    )
    
  • 验证容器端口与网络配置
    确认容器暴露的端口和Web App「配置」>「容器设置」中的「目标端口」一致(比如FastAPI默认的8000端口);检查Web App的「网络」配置,确保防火墙或VNet安全组没有阻止长连接流量。

  • 修正SSE输出格式
    SSE要求每个事件必须以data:开头,结尾用两个换行符(\n\n)分隔。修改生成通知的代码,手动拼接符合规范的输出:

    notif_data = Notification(
        id=str(uuid.uuid4()),  # 使用唯一ID避免重复处理
        event="completed_task",
        retry=MESSAGE_STREAM_RETRY_TIMEOUT,
        data=curr_notif,
    ).model_dump_json()
    yield f"data: {notif_data}\n\n"
    
  • 配置前端与后端CORS
    Vue前端初始化EventSource时,若跨域需开启withCredentials:

    const eventSource = new EventSource('/stream-notifs?user_id=' + userId, {
      withCredentials: true
    });
    

    后端FastAPI需配置CORS允许前端域名及凭证:

    from fastapi.middleware.cors import CORSMiddleware
    
    app.add_middleware(
        CORSMiddleware,
        allow_origins=["你的前端域名"],
        allow_credentials=True,
        allow_methods=["*"],
        allow_headers=["*"],
    )
    
  • 优化Azure Web App运行配置
    在Web App「配置」>「常规设置」中开启「始终开启」,避免应用因空闲被回收;通过「监控」>「日志流」查看实时请求日志,确认响应头是否正确返回、请求是否被截断;用Application Insights监控请求状态码与响应时间,排查超时问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 00:07:07