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

Python Flask视频流服务疑似内存泄漏,Docker内资源攀升致系统崩溃

内存占用持续攀升的核心根因
  • 无界队列设计缺陷:原代码中frameQueue = queue.Queue()默认无容量上限,若POST推帧速度高于视频流消费速度、或存在多个断开未清理的视频流连接,队列会持续堆积解码后的numpy格式帧(单帧1080p图像约占6MB内存),短时间堆积的帧会快速耗尽可用内存。同时这套队列的生产消费逻辑还会在多客户端访问时出现帧被分流、视频卡顿的问题,因为queue.get()取走即移除帧,多个并发的视频流生成器会共享同一批帧资源,无法给每个客户端推送完整的实时流
  • 流式连接无异常退出机制:每个访问/video_feed的客户端会触发独立的generate()生成器循环,当客户端主动断开连接(比如关闭浏览器标签)时,代码未捕获GeneratorExit等断开信号,循环会持续在后台运行,既不停消费队列帧做无效编码,也不释放对应的内存资源,最终形成僵尸进程式的资源堆积
  • 冗余设计不符合实时视频流特性:实时视频流仅需要展示最新的图像帧,不需要留存历史帧,用带缓存的队列存历史帧本身就是无效设计,没有存在的必要性
  • 开发服务器资源回收能力弱:直接用Flask自带的app.run()启动服务,这类开发服务器本身针对短时请求设计,处理长连接流式传输时,对应的线程资源回收逻辑存在天然缺陷,长时间运行必然出现内存泄漏
可落地的优化方案
  • 替换无界队列为单帧全局缓存:完全去掉队列,用带锁的全局变量存储最新一帧图像,从根源上杜绝多帧堆积的可能。比如改用如下逻辑:
import threading
# 替代原frameQueue
latest_frame = None
frame_lock = threading.Lock()

@app.route("/api/feed_image", methods=['POST'])
def read_image():
    global latest_frame
    nparr = np.frombuffer(request.data, np.uint8)
    frame = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
    with frame_lock:
        latest_frame = frame
    return "received"

def generate():
    global latest_frame, frame_lock
    while True:
        try:
            with frame_lock:
                outputFrame = latest_frame.copy() if latest_frame is not None else None
            if outputFrame is None:
                time.sleep(0.03)
                continue
            (flag, encodedImage) = cv2.imencode(".jpg", outputFrame)
            if not flag:
                continue
            yield(b'--frame\r\n' b'Content-Type: image/jpeg\r\n\r\n' + bytearray(encodedImage) + b'\r\n')
        except GeneratorExit:
            # 捕获客户端断开信号,直接退出循环释放资源
            break
        except Exception as e:
            print(f"视频流异常: {str(e)}")
            break

这种设计下,无论推帧速度多快、同时有多少个客户端看视频流,内存里永远只保留1帧图像的大小,不会出现持续内存上涨的问题,还能保证多客户端场景下每个流的完整性

  • 若仍要保留队列逻辑,必须设置严格的容量上限:如果业务确实需要少量帧做缓冲,就把队列设置为有界(比如queue.Queue(maxsize=2)),队列满时直接丢弃最旧的帧,绝对不能让队列无限增长
  • 替换开发服务器为生产级WSGI容器:把原来的app.run()启动逻辑替换为gunicorn + gevent的长连接友好型部署方式,不要用Flask自带的开发容器跑生产服务,启动命令参考:
    gunicorn -w 2 -k gevent -b 0.0.0.0:5001 app:app
  • 冗余代码清理:移除未使用的import(比如原代码里导入的azure.iot.device.aio.IoTHubModuleClient完全没在逻辑中使用),及时清理不再使用的numpy数组引用,帮Python垃圾回收及时释放内存
  • 去掉高频无意义日志:原代码中video_feed路由里的print(len(frameQueue))每次有请求就输出,不仅会刷爆Docker stdout日志占磁盘,也会额外增加内存开销,非必要不要在请求链路里打高频日志

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.24 13:45:05