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

如何清理超时请求以防止FastAPI服务内存泄漏?

解决FastAPI客户端超时后服务端请求未取消导致的内存泄漏问题

问题根源

客户端设置请求超时后,本地requests.post会抛出异常并终止等待,但Uvicorn默认不会主动检测客户端连接是否断开,服务端的请求处理任务(尤其是耗时的model.inference)会继续运行,占用内存无法释放,最终导致容器内存泄漏。

解决方案

1. 在请求处理中主动检测客户端连接状态

FastAPI的Request对象提供is_disconnected()方法,可判断客户端是否已断开连接。你可以在耗时操作的关键节点插入检查,一旦发现客户端断开就终止任务。

修改端点代码:

from fastapi import Request, HTTPException

@app.post("/extract", response_model=ExtractionResponse)
def extract(request: Request, req_data: ExtractionRequest) -> ExtractionResponse:
    # 先检查客户端状态
    if request.is_disconnected():
        raise HTTPException(status_code=499, detail="客户端已断开连接")
    
    # 如果模型推理支持插入中断检查,传入回调函数
    def check_connection():
        if request.is_disconnected():
            raise RuntimeError("客户端断开,终止推理")
    
    try:
        # 假设model.inference接受检查回调参数
        result = app.state.model.inference(req_data.sample, check_callback=check_connection)
    except RuntimeError as e:
        raise HTTPException(status_code=499, detail=str(e))
    
    return ExtractionResponse(sample=result)

如果模型推理是无法拆分的阻塞操作,用线程包装任务并定期轮询:

from fastapi import Request, HTTPException
import threading

@app.post("/extract", response_model=ExtractionResponse)
def extract(request: Request, req_data: ExtractionRequest) -> ExtractionResponse:
    result = None
    inference_error = None
    
    def run_inference():
        nonlocal result, inference_error
        try:
            result = app.state.model.inference(req_data.sample)
        except Exception as e:
            inference_error = e
    
    thread = threading.Thread(target=run_inference)
    thread.start()
    
    # 轮询检查客户端状态和线程完成情况
    while thread.is_alive():
        thread.join(timeout=0.5)
        if request.is_disconnected():
            # 需要模型支持主动终止(比如设置中断标志)
            app.state.model.abort_current_inference()
            raise HTTPException(status_code=499, detail="客户端断开,终止任务")
    
    if inference_error:
        raise inference_error
    
    return ExtractionResponse(sample=result)

2. 使用异步端点结合asyncio任务取消

如果模型推理可包装为异步操作(或用线程池异步执行),改用异步端点能更优雅地实现任务取消:

from fastapi import Request, HTTPException
import asyncio

@app.post("/extract", response_model=ExtractionResponse)
async def extract(request: Request, req_data: ExtractionRequest) -> ExtractionResponse:
    # 将同步推理包装为异步任务
    async def async_inference():
        loop = asyncio.get_event_loop()
        return await loop.run_in_executor(None, app.state.model.inference, req_data.sample)
    
    inference_task = asyncio.create_task(async_inference())
    disconnect_task = asyncio.create_task(request.is_disconnected())
    
    # 等待第一个完成的任务
    done, _ = await asyncio.wait(
        [inference_task, disconnect_task],
        return_when=asyncio.FIRST_COMPLETED
    )
    
    # 客户端断开则取消推理任务
    if disconnect_task in done:
        inference_task.cancel()
        raise HTTPException(status_code=499, detail="客户端已断开连接")
    
    result = inference_task.result()
    return ExtractionResponse(sample=result)

3. 优化Uvicorn启动参数

添加连接超时参数,配合端点内的检查减少无效连接占用:

CMD ["uvicorn", "--host", "0.0.0.0", "--port", "8080", "--timeout-keep-alive", "5", "main:app"]

--timeout-keep-alive设为5秒(和客户端超时一致),避免Uvicorn长时间保留已断开的连接。

关键注意事项

  • 如果模型推理是完全阻塞且不支持中断的,上述方案只能检测客户端断开,但无法强制终止任务,需修改模型代码加入中断检查逻辑(比如循环中检查全局标志)。
  • 499状态码是Nginx定义的"客户端关闭连接"状态,FastAPI支持返回该状态码标识客户端主动断开场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 08:10:00