性能比拼: Node.js vs Go

本内容是对知名性能评测博主 Anton Putra Node.js vs Go (Golang): Performance (Latency - Throughput - Saturation - Availability) 内容的翻译与整理, 有适当删减, 相关指标和结论以原作为准

在本篇内容中,我们将比较 Node.js 和 Golang。我会使用标准库在 Golang 中创建一个 Web 应用程序;而在 Node.js 中,我会使用非常常见的 Web 框架 --- Express

为了运行这些测试,我将两个应用程序都部署到了 AWS 上一个面向生产环境的 Kubernetes 集群中,并使用最新一代的 EC2 实例作为 Kubernetes 节点。

我们首先会测量 CPU 使用率、内存使用率、应用程序可用性、Kubernetes 的 CPU 限流情况以及网络压力。我们还会追踪每个应用程序接收和发送的字节数。这一数值可能会有很大差异,取决于请求头的数量以及应用程序是否启用了 KeepAlive(KeepAlive 允许一个 TCP 连接处理多个 HTTP 请求)。


第二个测试介绍

在第二个测试中,我引入了持久化层,因为大多数应用程序都需要以某种方式存储状态。例如,一个博客网站需要存储文章,一个电商网站需要存储库存,甚至一个待办事项列表也需要存储任务。

最常见的方法之一是使用数据库。在本次基准测试中,我们使用的是 PostgreSQL 关系型数据库。

除了前面提到的指标外,我们还会测量每个应用程序将数据插入数据库所需的延迟,并追踪连接池的状态以及每个应用程序如何扩展连接池。

作为一名 DevOps 工程师,我依赖开发者的输入来在生产环境中运行和优化应用程序。我欢迎任何改进应用的建议,甚至更好的是 Pull Request。你可以在视频描述中找到我 GitHub 仓库的链接


第一个测试

好了,现在我来部署这两个应用程序到 Kubernetes。你会注意到,Node.js 使用的 CPU 稍微多一些,而内存使用基本相同。好了,现在开始第一次性能测试。

我使用了一个 Kubernetes Job 和 20 个副本来为每个应用程序生成负载。整个测试可能持续了三到四个小时,但我会将其压缩为几分钟展示。我从每个 Pod 启动一个客户端开始,然后不断增加客户端数量,直到两个应用程序都开始失败。

另外,每个阶段之间我设置了 60 秒的间隔,客户端超时时间设置为 1 秒。当达到超时阈值时,你会在可用性图表中看到下降。

从一开始你就可以注意到,Node.js 使用更多的 CPU 来处理请求,且延迟明显高于 Go。我认为它的性能与我在之前视频中测试过的 Python Django 框架类似。除此之外,Node.js 使用了更多的内存,它默认还会发送更多的头信息,这也是你会看到它传输的数据更多的原因。

当我们达到每秒 9,000 个请求时,Node.js 的 CPU 使用率达到 60%,并陷入卡顿状态。如果你知道 Node.js 出现这种情况的原因,或者以前遇到过这种问题,请告诉我。在接下来的测试中,Node.js 的 CPU 使用率将维持在 60% 左右,而请求的延迟会持续增加。我知道你可以使用 cluster 模式,但我个人认为直接增加副本数量对 Node.js 更合适。

由于我使用的是与之前测试完全相同的设置,我们知道 Golang 可以处理大约 70,000 到 80,000 个请求。由于 Golang 的标准库没有任何速率限制机制,它会持续缓存所有请求,直到内存使用率达到 100%,然后被 Kubernetes 的 OOM(内存溢出)机制杀死。

在测试结束时,Node.js 也开始性能下降,很多请求开始超时,你可以在可用性图表中看到这一点。顺便说一句,我在 Node.js 中使用了 async 函数,并设置了 NODE_ENV=production

测试结果:Golang 可以处理大约 70,000 个请求,而 Node.js 只能处理大约 9,000 个请求。

现在让我打开整个测试周期中的每个图表:

  • 首先是每秒请求数图;
  • 然后是 P99 客户端延迟图;

Node.js 开始变慢前的延迟图;

  • 接下来是 CPU 使用图;
  • 内存使用图;
  • 可用性图;
  • CPU 限流图;
  • 最后是网络压力图。

好了,这就是第一个测试的全部内容。如果你有任何改进测试的建议,请告诉我。


第二个测试

现在我们进行第二个测试。在本测试中,我们向每个应用程序发送一个包含 JSON 负载的 POST 请求。应用程序会为设备生成一个 UUID,然后将其保存到数据库中。我为两个应用程序都设置了最大连接池大小为 20。

我使用一个开源的 Postgres Docker 镜像来运行一些数据库迁移操作。例如,我会创建一个用户,清除该用户的所有空闲连接,并创建一个表。然后我使用 init 容器在每次应用程序部署时运行这些迁移脚本。

现在开始测试。我为 Node.js 使用了最快的 Postgres 驱动之一,因此在测试初期,插入数据的延迟基本相同。整体延迟也非常接近。但当然,CPU 和内存使用差异很大。

你还可以注意到,连接池很快就达到了最大连接数 --- 每个应用程序 20 个连接。

当请求量达到每秒约 4,000 时,你会再次看到 Node.js 的 CPU 使用率达到 60%,并开始性能下降,延迟上升。看起来这就是 Node.js 在本测试中的最大处理能力。

继续测试,直到 Golang 开始失败。当请求量达到每秒约 7,000 时,你可以看到它开始卡顿,只有内存使用率继续上升。如果我们继续测试,它也会达到内存限制,并被 Kubernetes 杀死。

好了,现在让我打开整个测试周期中的每个图表:

  • 首先是每秒请求数图;
  • 客户端延迟图;
  • 数据库插入延迟图;
  • 连接池大小图;
  • CPU 使用率图;
  • 内存使用图;
  • 可用性图;
  • 最后是 CPU 限流图
相关推荐
二闹6 分钟前
三个注解,到底该用哪一个?别再傻傻分不清了!
后端
zzywxc78712 分钟前
详细探讨AI在金融、医疗、教育和制造业四大领域的具体落地案例,并通过代码、流程图、Prompt示例和图表等方式展示这些应用的实际效果。
开发语言·javascript·人工智能·深度学习·金融·prompt·流程图
大明8812 分钟前
用 mouseover/mouseout 事件代理模拟 mouseenter/mouseleave
前端·javascript
用户490558160812518 分钟前
当控制面更新一条 ACL 规则时,如何更新给数据面
后端
林太白19 分钟前
Nuxt.js搭建一个官网如何简单
前端·javascript·后端
晴空雨20 分钟前
一个符号让 indexOf 判断更优雅!JavaScript 位运算的隐藏技巧
前端·javascript
码事漫谈21 分钟前
VS Code 终端完全指南
后端
前端snow34 分钟前
前端无接口实现Table导出Excel的两种方案(附完整代码)
javascript·vue.js·react.js
该用户已不存在1 小时前
OpenJDK、Temurin、GraalVM...到底该装哪个?
java·后端
怀刃1 小时前
内存监控对应解决方案
后端