求助: Python flask 应用内存一直在增加 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
davinci21s
V2EX    Python

求助: Python flask 应用内存一直在增加

  •  
  •   davinci21s 2023-09-25 16:45:15 +08:00 3205 次点击
    这是一个创建于 798 天前的主题,其中的信息可能已经有所发展或是发生改变。

    2 个版本,都没有解决内存持续增加,由于内存一直增加,最后因为小鸡内存不足而被杀掉。

    这是一个加载训练后的模型,通过网络传入预测参数,然后返回预测 json 结果。

    可能同时发起(并发)几十个请求。

    求大佬帮忙看看问题出在哪里,谢谢。

    这是 chatgpt 4.0 给的版本

    from flask import Flask, request, jsonify import pickle import os import psutil import pandas as pd app = Flask(__name__) class SingletonModel: _instance = None def __new__(cls): if cls._instance is None: print("Creating Singleton Instance") cls._instance = super(SingletonModel, cls).__new__(cls) modelName = "xgboost_model-k.pkl" with open(modelName, "rb") as pkl_file: loaded_data = pickle.load(pkl_file) cls._instance.model = loaded_data['model'] cls._instance.scaler = loaded_data['scaler'] cls._instance.label_encoder = loaded_data['label_encoder'] cls._instance.feature_names = ['shortAvg','longAvg','volatility','diff'] return cls._instance resources = SingletonModel() model = resources.model scaler = resources.scaler label_encoder = resources.label_encoder @app.route('/predict', methods=['POST']) def predict(): global model, scaler, label_encoder data = request.json['input'] df = pd.DataFrame([data], columns=resources.feature_names) scaled_data = scaler.transform(df) prediction = model.predict(scaled_data) label_prediction = label_encoder.inverse_transform(prediction) return jsonify([label_prediction[0]]) if __name__ == '__main__': app.run(port=6601,debug=True) 

    这是 Claude 给的版本

     import asyncio from flask import Flask, request, jsonify import pickle import pandas as pd app = Flask(__name__) # 模型相关全局变量 model = None scaler = None label_encoder = None async def load_model(): global model, scaler, label_encoder,feature_names if not model: with open('xgboost_model-k.pkl', 'rb') as f: loaded_data = pickle.load(f) model = loaded_data['model'] scaler = loaded_data['scaler'] label_encoder = loaded_data['label_encoder'] feature_names = ['shortAvg','longAvg','volatility','diff'] async def predict(data): await load_model() df = pd.DataFrame([data], columns=feature_names) scaled_data = scaler.transform(df) prediction = model.predict(scaled_data) label_prediction = label_encoder.inverse_transform(prediction) return label_prediction[0] @app.route('/predict', methods=['POST']) async def predict_handler(): data = request.json['input'] result = await asyncio.gather(predict(data)) return jsonify(result) if __name__ == '__main__': app.run(port=6601,debug=False)  
    第 1 条附言    2023-09-26 10:49:34 +08:00

    使用常规uWSGI也无法解决内存持续增加问题,但是,

    配置一个参数可以完美的解决。

    --max-worker-lifetime 参数,它允许你设置 worker 进程的最大生命周期(以秒为单位), 到达这个时间限制后,worker 进程将被优雅地重启。

    --reload-on-rss 如果一个 worker 使用超过限定 的内存,它将被重启。

    img

    20 条回复    2023-09-27 10:35:01 +08:00
    missz
        1
    missz  
       2023-09-25 17:10:13 +08:00
    我用 flask 启的 yolo5 的接口也是内存无限增长,用 memory_profiler 也看不出具体增长原因,现在是用个 shell 脚本超过一定内存就 kill 重启
    jstony
        2
    jstony  
       2023-09-25 17:35:17 +08:00
    换个版本,cpython 的底层还是 c ,就不能避免完全没有内存泄漏,而且一大堆库,质量良莠不齐。
    davinci21s
        3
    davinci21s  
    OP
       2023-09-25 17:45:43 +08:00
    @missz 无奈中


    @jstony thanks ,我试试。
    wynemo
        4
    wynemo  
       2023-09-25 20:18:06 +08:00
    用 uwsgi ,flask 自带的就是这样
    Inzufu
        5
    Inzufu  
       2023-09-25 22:19:25 +08:00 via Android
    跑大项目不建议用 py ,还是 nodejs 靠谱一点儿,这两个语言其实学起来差不多。
    roycestevie6761
        6
    roycestevie6761  
       2023-09-25 22:28:42 +08:00
    python 就这样的啦
    among
        7
    among  
       2023-09-25 22:34:54 +08:00
    uwsgi ,多进程模式,配置超过多少内存,就 fork 一个新的进程。
    ynkkdev
        8
    ynkkdev  
       2023-09-25 22:35:02 +08:00   1
    @inzufu 这玩意一眼就是算法推理服务,nodejs 加载模型文件跑算法推理,你这不是难为算法同学吗?

    这代码里面一眼看去就是模型资源全局变量跑,十几个并发,要是没有 gpu 没有显存,就是需要大内存的。再就是推理接口一般也都是 batch 推理,并发可以利用 batch 来缓解压力。

    简而言之,要么懂算法、要么懂 pthon 后端,两者都不精通,算法服务能跑起来已经谢天谢地,就不要苛责性能了
    Inzufu
        9
    Inzufu  
       2023-09-25 22:43:25 +08:00 via Android
    @youngce 不好意思,我确实没看代码,抱歉抱歉
    Mystery0
        10
    Mystery0  
       2023-09-26 09:49:35 +08:00 via Android
    蹲一个,我也遇到这个问题,照着别人的代码训练了一个模型然后用 flask 提供接口调模型预测数据
    运行之后内存就会慢慢变大,现在的解决办法是隔段时间看一下内存,超过 6-700mb 就重启一下
    davinci21s
        11
    davinci21s  
    OP
       2023-09-26 10:51:12 +08:00
    @missz
    @jstony
    @wynemo
    @inzufu
    @roycestevie6761
    @among
    @Mystery0

    感谢各位,谢谢楼上提醒,uwsgi 设置参数可以完美解决,我设置内存超过 200M 自动重启。
    yagamil
        12
    yagamil  
       2023-09-26 12:12:54 +08:00
    model, scaler, label_encoder,feature_names
    这几个变量用全局, 如果不同请求过来, 里面的模型一些参数会被其他进程的请求给修改掉么?
    wxlpure
        13
    wxlpure  
       2023-09-26 12:52:29 +08:00
    flask 不是同步框架吗?同步框架内用异步是啥效果?
    davinci21s
        14
    davinci21s  
    OP
       2023-09-26 13:56:50 +08:00
    @yagamil 不是很懂,chatgpt 帮我写的


    @wxlpure Claude 帮我写的,这应该相当于队列吧。
    julyclyde
        15
    julyclyde  
       2023-09-26 17:09:19 +08:00
    @inzufu 如果只给出建议但不说为什么
    那我认为这其实是个宗教式的回答
    nonduality
        16
    nonduality  
       2023-09-26 17:11:48 +08:00
    对 Claude 写的不评价,对 ChatGPT 写的说点看法。

    单例模式在这里应该是没用的,你可以把 print 的内容改为输出到日志( logging.info ),然后在日志里查看是否不断创建 SingletonModel 。

    解决方案之一是利用 RPC:在后台启动一个常驻的数据处理服务,负责接收请求、数据处理和返回结果; Flask 负责把客户端请求转发到常驻服务,再把返回来的结果发给客户端。

    需要注意的是,RPC 有多种执行模式,你要避免不断 fork 进程或 spawn 线程去处理数据,这样内存占用也有可能不断膨胀,而要直接调用数据处理的入口函数,这时候单例模式就能起作用。
    davinci21s
        17
    davinci21s  
    OP
       2023-09-26 17:29:15 +08:00
    @nonduality 感谢,可以对遇到相同问题的提供参考。
    subjadeites
        18
    subjadeites  
       2023-09-26 18:32:58 +08:00 via Android
    用 gunicorn 试试?
    subjadeites
        19
    subjadeites  
       2023-09-26 18:33:46 +08:00 via Android
    import gevent.monkey

    gevent.monkey.patch_all()

    import multiprocessing

    debug = False
    loglevel = 'info'
    bind = '0.0.0.0:7000'
    pidfile = 'logs/gunicorn.pid'
    logfile = 'logs/debug.log'

    # 启动的进程数
    workers = multiprocessing.cpu_count() * 2
    worker_class = 'gunicorn.workers.ggevent.GeventWorker'
    preload_app = True

    x_forwarded_for_header = 'X-FORWARDED-FOR'
    zheng5200
        20
    zheng5200  
       2023-09-27 10:35:01 +08:00 via iPhone
    gunicorn 起 flask 也有这个问题,也是通过--max-requests 解决的,()
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     964 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 25ms UTC 19:18 PVG 03:18 LAX 11:18 JFK 14:18
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86