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

Fra*_*nko 12 ubuntu nginx node.js mongodb redis

正如问题标题所暗示的那样,我很难弄清楚可以在我的应用程序上进行哪些改进(或在 os、ubuntu 中进行调整)以达到可接受的性能。但首先我将解释架构:

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

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

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

我现在发现nodefly很棒,所以我终于可以看到我的生产服务器上发生了什么,并且我从几天开始收集数据。如果有人想查看图表,我可以让您访问,但基本上我可以看到我有 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
Run Code Online (Sandbox Code Playgroud)

这表明我在 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
Run Code Online (Sandbox Code Playgroud)

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

我主要是一名 javascript 程序员,而不是系统管理员,所以我不知道我的服务器应该处理多少负载,但肯定它没有按预期执行。否则该应用程序是稳定的,最后一个问题阻止我发布准备好的应用程序的移动版本,因为显然它们会带来更多负载并最终使整个程序崩溃!

希望有一些明显的我做错了,有人会帮助发现它......随时向我询问更多信息,我很抱歉问题的长度,但我相信这是必要的......提前致谢!

Fra*_*nko 11

经过几天的反复试验和错误,我很高兴能够说我已经了解瓶颈在哪里,我将把它张贴在这里,以便其他人可以从我的发现中受益。

问题在于我与 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 实现某种连接池,以进一步优化它,但我不确定这是否不会再次导致 pub/sub 事件在连接上建立,除非它们中的每一个每次都被摧毁和重建,以清洁它们。

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

干杯。

  • 我在我的生产应用程序中遇到了同样的问题,也是服务器管理员角色的新手。我遵循你在概念上所做的,但我有一些关于如何去做的问题 - 也许你可以在你接受的答案中提供一些资源的链接?或者只是提供更多信息?特别是关于“但后来我决定尝试为每个客户端创建 2 个到 redis 的新连接,以便仅在其会话上处理其发布/订阅,然后在用户断开连接后关闭连接。” (2认同)