node.js、mongodb、redis,在 ubuntu 生产中性能下降,RAM 是空闲的,CPU 100%

node.js、mongodb、redis,在 ubuntu 生产中性能下降,RAM 是空闲的,CPU 100%

正如问题标题所示,我很难弄清楚我的应用程序可以改进什么(或在操作系统 ubuntu 中进行调整)才能获得可接受的性能。但首先我将解释一下架构:

前端服务器是一台 8 核机器,8GB RAM,运行 Ubuntu 12.04。该应用程序完全用 javascript 编写,并在 node.js v 0.8.22 中运行(因为有些模块似乎对较新版本的节点有抱怨)我使用 nginx 1.4 将来自端口 80 和 443 的 http 流量代理到使用节点集群 api 管理和启动的 8 个节点工作器。我使用最新版本的 socket.io 0.9.14 来处理 websocket 连接,我在其上仅启用了 websockets 和 xhr-polling 作为可用传输。在这台机器上,我还运行了一个 Redis(2.2) 实例

我将持久数据(如用户和分数)存储在 mongodb(3.6)上的第二台服务器上,该服务器具有 4GB RAM 和 2 个核心。

该应用程序几个月前就开始投入生产(几周前它一直在一台机器上运行),每天有大约 18000 名用户使用它。除了一个主要问题:性能下降之外,它一直运行良好。随着使用,每个进程使用的 CPU 数量不断增长,直到它使工作进程饱和(不再处理请求)。我暂时解决了这个问题,每分钟检查每个工作进程的 CPU 使用情况,如果达到 98%,就重新启动它。所以这里的问题主要是 CPU,而不是 RAM。自从我更新到 socket.io 0.9.14(早期版本有内存泄漏)后,RAM 不再是问题,所以我怀疑这是一个内存泄漏问题,特别是因为现在是 CPU 增长相当快(我必须每天重新启动每个工作进程 10-12 次!)。说实话,RAM 使用情况也在增长,但非常缓慢,每使用 2-3 天增加 1 GB,奇怪的是,即使我完全重新启动整个应用程序,它也不会释放。只有我重新启动服务器它才会发布!这个我真的无法理解......

我现在发现节点飞行这真是太棒了,所以我终于可以看到我的生产服务器上发生了什么,而且我已经收集了几天的数据。如果有人想看图表,我可以给你访问权限,但基本上我可以看到我有 80 到 200 个并发连接!我预计 node.js 可以处理数千个请求,而不是数百个请求。此外,http 流量的平均响应时间在 500 到 1500 毫秒之间,我认为这真的很多。此外,此时有 1300 个用户在线,这是“ss -s”的输出:

Total: 5013 (kernel 5533)
TCP:   8047 (estab 4788, closed 3097, orphaned 139, synrecv 0, timewait 3097/0), ports 0

Transport Total     IP        IPv6
*         5533      -         -
RAW       0         0         0
UDP       0         0         0
TCP       4950      4948      2
INET      4950      4948      2
FRAG      0         0         0

这表明我在 timewait 中有很多关闭的连接。我已将最大打开文件数增加到 999999,以下是 ulimit -a 的输出:

core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 63724
max locked memory       (kbytes, -l) 64
max memory size         (kbytes, -m) unlimited
open files                      (-n) 999999
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 8192
cpu time               (seconds, -t) unlimited
max user processes              (-u) 63724
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited

因此,我认为问题可能出在 http 流量上,由于某些原因,可用的端口/套接字饱和了(?),但有一件事对我来说没有意义:为什么当我重新启动工作程序,并且所有客户端在几秒钟内重新连接时,工作程序的 CPU 负载会下降到 1%,并且能够正常处理请求,直到大约 1 小时后(高峰时段)饱和?

我主要是一名 javascript 程序员,而不是系统管理员,所以我不知道我的服务器应该处理多少负载,但它肯定没有达到应有的性能。除此之外,应用程序很稳定,最后一个问题阻止我发布已准备好的移动版应用程序,因为它们显然会带来更多负载并最终导致整个应用程序崩溃!

希望我做错了一些事情,并且有人能帮助发现它...请随时向我询问更多信息,我很抱歉问题这么长,但我相信这是必要的...提前谢谢!

答案1

经过几天的反复试验,我很高兴地说我已经了解了瓶颈所在,我会将其发布在这里,以便其他人可以从我的发现中受益。

问题在于我与 socket.io 一起使用的发布/订阅连接,特别是 socket.io 用于处理套接字实例进程间通信的 RedisStore。

在意识到我可以使用 redis 轻松实现我自己的 pub/sub 版本后,我决定尝试一下,并从 socket.io 中删除了 redisStore,保留默认的内存存储(我不需要向所有连接的客户端广播,而只需要在可能连接在不同进程上的 2 个不同用户之间广播)

最初,我仅声明了 2 个全局 redis 连接 x 进程来处理每个连接客户端上的发布/订阅,应用程序使用的资源较少,但我仍然受到 CPU 使用率持续增长的影响,因此没有太大变化。但后来我决定尝试为每个客户端创建 2 个新的 redis 连接,以便仅在其会话中处理其发布/订阅,然后在用户断开连接后关闭连接。然后在生产中使用一天后,CPU 仍然在 0-5%... 宾果!没有进程重新启动,没有错误,具有我期望的性能。现在我可以说 node.js 很棒,很高兴选择它来构建这个应用程序。

幸运的是,redis 被设计用于处理许多并发连接(与 mongo 不同),默认情况下它设置为 10k,在单个 redis 实例上为大约 5k 个并发用户留下了空间,这对我来说目前已经足够了,但我读到它可以被推高到 64k 个并发连接,所以我相信这种架构应该足够稳固。

此时我正在考虑为 redis 实现某种连接池,以进一步优化它,但不确定这是否不会再次导致发布/订阅事件在连接上建立,除非每次都销毁并重新创建它们以清理它们。

无论如何,感谢您的回答,我很想知道您的想法,以及您是否有任何其他建议。

干杯。

答案2

您有源代码可以转储吗?可能是数据库连接未关闭?进程正在等待永不关闭的 HTTP 连接。

您能发布一些日志吗?

执行 ps -ef 并确保没有任何东西仍在运行。我见过 Web 进程留下僵尸进程,除非您执行 kill -9 ,否则它们不会消失。有时关机不起作用或无法完全起作用,这些线程或进程会占用 RAM,有时还会占用 CPU。

它可能是代码中某处的无限循环,或者是持有数据库连接的崩溃进程。

您使用了哪些 NPM 模块?它们都是最新的吗?

你捕获到了异常吗?请参见: http://geoff.greer.fm/2012/06/10/nodejs-dealing-with-errors/ 看: https://stackoverflow.com/questions/10122245/capture-node-js-crash-reason

一般提示:

http://clock.co.uk/tech-blogs/preventing-http-raise-hangup-error-on-destroyed-socket-write-from-crashing-your-nodejs-server

http://blog.nodejitsu.com/keep-a-nodejs-server-up-with-forever

http://hectorcorrea.com/blog/running-a-node-js-web-site-in-production-a-beginners-guide

https://stackoverflow.com/questions/1911015/how-to-debug-node-js-applications

https://github.com/dannycoates/node-inspector

http://elegantcode.com/2011/01/14/taking-baby-steps-with-node-js-debugging-with-node-inspector/

答案3

这本身并不是一个答案,因为您的问题更像是一个故事,而不是一个指出一个答案的问题。

只是想说,我成功构建了一个带有 socket.io 的 node.js 服务器,可以处理超过 100 万个持久连接,消息有效负载平均为 700 字节。

1Gbps 的网络接口卡一开始就处于饱和状态,我看到从发布事件到所有客户端都有大量的 I/O 等待。

删除 nginx 的代理角色也浪费了宝贵的内存,因为要仅使用一台服务器实现一百万个持久连接,需要调整配置、应用程序和操作系统参数,这是一项艰巨的工作。请记住,这只有在有大量 RAM 的情况下才可行(大约 1M 个 websockets 连接会占用大约 16GB 的 RAM,对于 node.js,我认为使用 sock.js 是低内存消耗的理想选择,但目前,socket.io 消耗了那么多内存)。

此链接是我使用 node 实现该连接量的起点。除了它是一个 Erlang 应用程序之外,所有操作系统调优几乎都与应用程序无关,并且应该适用于任何旨在实现大量持久连接(websockets 或长轮询)的人。

高血压,

相关内容