用 Go 重写 Node.js 服务:项目性能提升 5 倍,内存减少 40%原创
Hasura Storage 是一项开源服务,在 hasura 和任何 s3 兼容的存储服务之上增加了一个存储服务。其目的是能够利用云存储服务,同时也利用 hasura 的功能,如它的 graphql API、权限、行动、预设等。
出于业务发展需求,Hasura Storage 团队近期将其原本用 Node.js 编写的服务用 Golang 进行了重写。“这个用 Node.js 编写的服务在相当长的一段时间内为我们提供了良好的服务,但随着公司的发展和用户数量的大规模增加,性能开始成为一个问题。虽然 Node.js 可能有很多可取之处,但优异的性能和可扩展性并不是其中之一。”
Hasura Storage 方面表示,在使用 Golang 进行重写后,其可处理的服务请求数增加了 5 倍,同时内存消耗减半。根据介绍,他们选择 Go 的原因在于:
- 该语言的依赖性管理系统和构建系统使其非常适合云
- 团队有丰富的 Golang 经验
- 虽然 Go 是一种非常冗长的语言(尤其是与 Node.js 相比),但它非常易于学习且编写速度快
- 性能非常优异
重写完成后,Hasura Storage 团队针对 Node.js 和 Golang 版本的服务运行了一些基准测试。使用了 k6 并设计了以下测试:
当测试开始时,它会在前 10 秒内将 workers 的数量从 1 增加到 TARGET
然后再运行 60 秒才结束。
Workers 尽可能快地查询服务
运行以下测试:
download_small_file(100 workers ) - 下载一个 100KB 的文件
download_medium_file(100 workers )- 下载一个 5MB 的文件
download_large_file(50 workers )- 下载 45 MB 文件
download_image(100 workers )- 下载 5.3 MB 的图片
download_image_manipulated(10 workers)- 下载相同的图像,但调整图像大小并即时应用一些模糊
CPU 被限制在整个系统的 10%
RAM 是无限的
Hasura Storage 提前声明称,最终结果不应该只看表面的数字;“用于基准测试的系统的 CPU 容量非常有限,因为我们想对这两种服务施加压力并看看它们在压力下的表现如何所以,我们感兴趣的不是数字,而是两个版本之间的差异。”
测试结果表明,Hasura Storage 在每种情况下能够处理的请求数都实现了大幅提升,其中较小的文件(5x)的效果更为显著。
同时在所有情况下都设法大大改善了 RAM 消耗,尤其是在下载大文件时。值得一提的是,这还是在提供了多达 5 倍的请求的前提下。
另一个重要的指标是响应时间,Hasura Storage 提供了两个数据:最小响应时间,开源告诉我们系统未承受压力时的响应时间;以及 P95,开源告诉我们大多数用户的响应时间最多是多少(包括当系统处于压力之下)。
首先是最小响应时间。测试用例 download_small_file 的结果不好从图中目测,但 Hasura Storage 称其将场景的响应时间从 Node.js 用例的 29ms 提高到 Golang 用例的 7ms。除了在 download_image_manipulated 中实现了大约 2 倍的改进外,在其他场景中则均实现了 4 倍的改进。
再是 P95。除 download_image_manipulated 和 download_large_file 外,大多数情况下都实现了 4 倍的改进。Hasura Storage 解释称,虽然没有像其他情况那样戏剧性,但这两种情况下都有实质性的改进。“这是合理的,因为下载大文件会受到 I/O NET 的约束,而处理图像则会受到 CPU 的约束。但即使如此,我们也很高兴看到这种实质性的改进。”
此外,图像处理方面也有所改善。
在服务被重写和测试后,Hasura Storage 将服务部署到了生产环境,一些重写的好处也开始展现。如下图所示(集群的一个节点中的 RAM 使用情况),内存占用减少了近 40%。“这是一项重大改进,可以让我们在不增加整体基础设施费用的情况下为更多用户和流量提供服务。”
Hasura Storage 方面表示,他们决定重写服务是为了提高性能指标;而在对两个服务进行并列基准测试后,他们也可以有底气的宣称成功地显着改善了所有指标。“我们希望能够在使用更少资源的同时满足更多请求,同时还可以改善我们用户的响应时间,我相信他们会喜欢的。”