终于找到写的 NodeJS 微服务都支持不到 1 天的原因了 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
doubleflower
V2EX    Node.js

终于找到写的 NodeJS 微服务都支持不到 1 天的原因了

  •  3
     
  •   doubleflower 2017-10-08 07:47:37 +08:00 14965 次点击
    这是一个创建于 3001 天前的主题,其中的信息可能已经有所发展或是发生改变。

    前情: t/392238

    从 Python 转过来写了几个 Node 微服务全它妈的跑一天就 Out of memory,之前暂时用了高内存就主动自杀的补丁顶着。

    然后这二天查了相关文章,发现 Node 的 gc 和 Python 是完全不同的,Python 的 gc 相当主动(可能和引用计数有关),一有无用内存当场就回收了,而 Node 不会(类似于 Java )。 默认 64 位机器 Node gc 认为你有 1.4G 内存,到这个点之前 gc 干活相当偷懒,可是我都是跑在 1G 小鸡上的,所以就 BOOM 了。

    所以你是在内存小于 1G 的机器上跑比较忙的需长时间运行的程序,需要在 Node 命令行上加上 --max_old_space_size=384 --optimize_for_size 这二参数,我跑了几天内存都没过 400M,而以前没多长时间就超 700M 引起 OOM。

    38 条回复    2017-10-11 09:38:39 +08:00
    iwj
        1
    iwj  
       2017-10-08 08:06:15 +08:00 via iPhone
    有意思,我跟楼主情况差不多,也刚写 Node,估计也会遇到这个问题:)
    opengps
        2
    opengps  
       2017-10-08 08:07:56 +08:00 via Android
    新手处理不到这么深的 bug...
    y835L9DyC5XD09kq
        3
    y835L9DyC5XD09kq  
       2017-10-08 08:32:37 +08:00 via iPhone
    点赞
    janxin
        4
    janxin  
       2017-10-08 08:37:02 +08:00
    确实没机会跑在 1G 的小鸡上...
    zjqzxc
        5
    zjqzxc  
       2017-10-08 09:30:41 +08:00
    感谢楼主。。我好像明白为啥之前写的 nodejs 版的 ddns 服务器跑再 vps 上几天就挂了,而 python 版一直很坚挺。。
    timothyye
        6
    timothyye  
       2017-10-08 09:46:10 +08:00 via Android
    blanu
        7
    blanu  
       2017-10-08 10:33:25 +08:00
    阅读一下《深入浅出 node.js 》很快就能找到答案了。。。。
    lilydjwg
        8
    lilydjwg  
       2017-10-08 12:05:19 +08:00
    CPython 使用引用计数,没有循环引用的话会立即回收的。Javascript、Java (以及 Go )都是经常性地找有没有垃圾,所以会不及时。Python 文档中也有提及,不要依赖对象立即被销毁,因为在其它实现里不一定如此。

    不过我也是第一次听说有人使用 NodeJS 遇到 gc 的问题。绝大部分遇到 gc 问题的都是 Go 用户,绝大部分调 gc 参数的都是 Java 用户呀。
    janxin
        9
    janxin  
       2017-10-08 12:09:22 +08:00
    @lilydjwg 其实很多 Go 用户现在也遇不到 GC 问题啊...
    lilydjwg
        10
    lilydjwg  
       2017-10-08 12:14:20 +08:00
    @janxin #9 名气大的除了 Google 和 Cloudflare 我都听说在折腾 gc 了,有的已经放弃 Go 了,有的还在挣扎。
    lilydjwg
        11
    lilydjwg  
       2017-10-08 12:15:19 +08:00
    @janxin #9 而且我说的是遇到 gc 问题的用户里 Go 用户多。原命题成立逆命题不一定成立嘛。
    gongzili456
        12
    gongzili456  
       2017-10-08 13:19:58 +08:00
    善用内存.......
    janxin
        13
    janxin  
       2017-10-08 13:28:50 +08:00 via iPhone
    @lilydjwg 没错没错
    jun4rui
        14
    jun4rui  
       2017-10-08 15:03:18 +08:00
    用 PM2 啊,然后选择一个时间间隔自动 load 一下(相当于重启但服务不会停),这样能保证不出问题。然后你就有时间慢慢查了
    coderfox
        15
    coderfox  
       2017-10-08 15:34:40 +08:00 via Android
    我在 700M 机器上用 docker 跑 node,64 位,没感觉到 gc 的问题哇。
    是不是不同版本的表现不同?
    kimown
        16
    kimown  
       2017-10-08 15:40:49 +08:00 via Android
    楼主有没有想过是写的程序有问题 ?
    workwonder
        17
    workwonder  
       2017-10-08 15:49:01 +08:00 via Android
    @lilydjwg 根据普遍的反映,Python 在内存方面并不省心。 据说 Python 程序一旦在某个时刻占用了很高的内存,后面就下不来了。而我的多次实践也未能幸免于此,我的 Django 应用不得不启用 max-requests + max-memory 来不断采用新的 worker 来轮回,另一个 flask 应用里面的 celery workers 也不得不根据消费的任务数量来自杀轮回。关于此大神能否谈谈看法?
    可以试试关键词 python memory leak not release 看看相关材料。
    lilydjwg
        18
    lilydjwg  
       2017-10-08 15:50:52 +08:00
    @workwonder #17 是的,请小心使用旧版本。
    wsy2220
        19
    wsy2220  
       2017-10-08 15:51:23 +08:00
    感觉程序有问题的可能性比较大
    workwonder
        20
    workwonder  
       2017-10-08 16:00:33 +08:00 via Android
    @lilydjwg 有没有关于此方面讲的比较透测的资料。
    关于版本,我用 python 3.5 感觉泄露问题还很严重啊。
    lilydjwg
        21
    lilydjwg  
       2017-10-08 16:09:51 +08:00
    @workwonder #20 你可以看看 gc.garbage 里有没有东西。然后试试 tracemalloc 和 objgraph、memory_profilter 之类的工具。我有一些长期工作的 Python 程序,仅有一个偶尔会出现内存泄漏(并且需要数月才能明显观察到)。
    shiny
        22
    shiny  
    PRO
       2017-10-08 16:14:45 +08:00
    我写的 node 服务经常跑在 1G 或者更小的鸡上也没遇到过这个问题,只处理过内存泄露的情况。
    workwonder
        23
    workwonder  
       2017-10-08 16:16:39 +08:00 via Android
    @lilydjwg 我的 Python 程序都是靠自杀+接力来实现长期运行的,搞不搞笑,我觉得很羞辱,曾经拒绝启用这样的参数(比如 gunicorn 或 uwsgi 里面),后来不得不妥协了。
    lilydjwg
        24
    lilydjwg  
       2017-10-08 16:21:00 +08:00
    @workwonder #23 还好吧,nodejs 和 php-fpm 都是这么干的。
    doubleflower
        25
    doubleflower  
    OP
       2017-10-08 17:42:36 +08:00
    我想要说明的是,不是我写的所有 node 应用都撑不到一天。

    二个 HTTP Web 服务可以撑相当长时间(一个月起)。

    而另三个队列处理的就不行。不过逻辑和 http server 有点不同,主要是处理响应比较慢,不是当场返回结果,而是会在内部有一个内存列队,并用了大量 Promise 异步(刚转到 node 这点很爽,用得没有节制)。我一开始以为用的异步太多了导致的内存泄漏,现在看来并不是。

    猜想是简单的 http 服务请求内存分配释放很快,总是在新生代。而处理队列+大量 promise 基本都是在老生代,所以有区别。
    simple2025
        26
    simple2025  
       2017-10-08 17:50:32 +08:00
    @workwonder 你的 django 程序是不是用了 celery?可能是 celery 配置不当导致的内存泄漏,你有没有设置
    BROKER_POOL_LIMIT = 0 这个东西,设置为 0 会内存泄漏
    workwonder
        27
    workwonder  
       2017-10-08 18:33:26 +08:00 via Android
    @chenqh 我刚举的例子,Django 项目没用 celery,flask 项目配合 celery 使用的。
    多谢你的设置指示!
    simple2025
        28
    simple2025  
       2017-10-08 18:44:51 +08:00
    @workwonder 好吧,那我就不知道了,有可能真的是 py3.5 的问题吧
    janxin
        29
    janxin  
       2017-10-08 19:51:12 +08:00
    @workwonder 你这个应该也是看具体情况具体分析了,我某个线上应用目前运行 200+天没有大量的内存泄漏情况出现...
    workwonder
        30
    workwonder  
       2017-10-08 19:58:39 +08:00 via Android
    @janxin 我那个 Django 应用的并发量和日访问量都很小,问题是数据量很大,经常是上万条记录(再加上关联查询)一起 load 到前端,实际上一两次这样的请求消耗的内存总量并不大,要命的是内存不及时释放(或者根本不释放),持续上涨一直到爆。
    workwonder
        31
    workwonder  
       2017-10-08 20:04:42 +08:00 via Android
    @workwonder 我也知道 Django 可以用迭代器模式遍历 queryset,这样能大大优化内存占用。但我觉得这有点自欺欺人(减少单次请求的内存消耗,并没有实际解决内存的不释放问题,顶多是缓兵之计),而且我是配合 drf 使用,改成迭代器模式不太自然。
    dzmcs
        32
    dzmcs  
       2017-10-09 01:27:09 +08:00 via iPad
    我都半年没动过 node 写的程序了,感觉挺稳定的
    calease
        33
    calease  
       2017-10-09 10:54:05 +08:00
    python 那个不是泄露。python interpreter 不会把从系统申请到的内存返回给系统,所以给人“越用占内存越多”的感觉。比如你写一个最大支持 1G 上传的服务器,并且用最大 25 个 threads,那么高峰时期你 python process 的最大内存占用会是 25G+,但并不会增大到无限多。所以在用 python 写大内存需求的程序时需要仔细考虑,如果内存 intensive 的操作要加锁。
    marvinwilliam
        34
    marvinwilliam  
       2017-10-09 12:40:16 +08:00
    你这个内存泄漏没处理好吧? 我们这的项目跑起来内存占用一直在一个稳定的范围之内啊.
    powerfj
        35
    powerfj  
       2017-10-09 17:43:37 +08:00
    额, 应该是哪个模块泄漏了, 理论上你的服务正常跑的话不可能内存一直涨的, 我的服务都是几个月不重启都不会内存炸的..
    doubleflower
        36
    doubleflower  
    OP
       2017-10-09 17:46:36 +08:00
    @powerfj 并不是你说的这样,因为我做了这个设置后内存一直都没有涨。没做这前一直会涨到 OOM。
    imherer
        37
    imherer  
       2017-10-09 21:20:47 +08:00
    @doubleflower #25 一样。 之前用 http 的东西跑个大半年都没任何问题,最近用 socket 做的内存涨的太快了,还在苦逼的查找问题中。。。
    powerfj
        38
    powerfj  
       2017-10-11 09:38:39 +08:00
    @doubleflower 开始没仔细看, 大概明白你的意思了, 这么说就是 node 设置的最小的内存大小超过了你的服务器的内存最大值, 所以会存在这个问题.
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2911 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 26ms UTC 11:03 PVG 19:03 LAX 03:03 JFK 06:03
    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