V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
doubleflower
V2EX  ›  Node.js

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

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

    前情: https://www.v2ex.com/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...
    kangkang
        3
    kangkang  
       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  
       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 基本都是在老生代,所以有区别。
    chenqh
        26
    chenqh  
       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 使用的。
    多谢你的设置指示!
    chenqh
        28
    chenqh  
       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   ·   实用小工具   ·   5865 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 06:27 · PVG 14:27 · LAX 22:27 · JFK 01:27
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.