Code前端首页关于Code前端联系我们

如何优化 Golang 服务,使 CPU 使用率降低 40% 以上

terry 2年前 (2023-09-24) 阅读数 56 #后端开发

十年前,Google 面临着 C++ 编译时间过长带来的严重瓶颈,需要一种全新的方式来解决问题。谷歌的工程师夫妇通过创建一种名为 Go(也称为 Golang)的新语言来应对这一挑战。 Go 这种新语言提供了 C++ 的优点(特别是性能和稳定性),并结合了 Python 的速度,使 Go 能够快速利用多个内核,同时实现并发。

在Coralogix(Go语言中文网站翻译:提供全面日志分析的服务产品,官网[1]),我们努力为客户提供实时分析、警报和元数据改进他们的日志以进行解析。在解析阶段,我们需要非常快速地解析多行复杂的服务日志,这个目标是我们决定使用Golang的原因之一。

这项新服务现在 24/7 全天候运行。虽然我们已经看到了非常好的结果,但它还需要在功能强大的机器上运行。该 Go 服务运行在具有 8 个 CPU 和 36 GB 配置的 AWS m4.2xlarge 实例上,每天解析数十亿条日志。

在这个阶段一切都很顺利,我们本可以自我感觉良好,但这不是我们在 Coralogix 想要的表现。我们想要更多的功能,比如性能等,或者使用更少的 AWS 实例。要改进,我们首先必须了解瓶颈的本质以及如何减少或彻底解决它们。

我们决定对我们的服务进行分析,以检查到底是什么导致了高 CPU 使用率,并看看我们是否可以对其进行优化。

首先我们将Go升级到最新的稳定版本(这是软件生命周期中至关重要的一步)。我们使用的是Go 1.12.4版本,最新的是1.13.8。根据文档[2],Go 1.13 版本在运行时库和其他一些主要利用内存使用的组件方面取得了长足的进步。总之,使用最新的稳定版本可以为我们节省很多工作。

因此,内存消耗减少了,从大约 800 MB 减少到仅 180 MB

其次,我们开始进行分析,以更好地了解我们的流程并找出我们应该把时间和资源花在哪里。

分析不同的服务和编程语言可能看起来很复杂和令人生畏,但使用 Go 实际上非常简单,只需几条命令即可描述。 Go 有一个名为“pprof”的特殊工具,可以通过侦听路由(默认端口 6060)来应用到您的应用程序,并使用 Go 数据包来管理 HTTP 连接:

import _ "net/http/pprof"

然后在您的 main 函数或初始化如下路由包:

go func() {
 log.Println(http.ListenAndServe("localhost:6060", nil))
}()

现在您可以启动服务并连接到:

http://localhost:6060/debug/pprof

Go 官方的完整文档可以在这里找到[3]。

pprof 的默认配置是每 30 秒测量一次 CPU 使用率。有许多不同的选项可以测量 CPU 使用率、堆使用率等。

我们主要关注 CPU 使用情况,因此我们在生产环境中进行了 30 秒的分析,发现了下图所示的内容(提醒:这是在我们升级 Go 版本并将 Go 内部迁移后的结果最小化后的结果)组件):如何优化 Golang 服务来减少 40% 以上的 CPU进行分析 — Coralogix

如您所见,我们发现了大量运行时库活动:GC 使用了几乎 29% 的 CPU(这只是最常用的前 20 个)对象)。由于 Go 的 GC 非常快且高度优化,因此最好不要更改或修改它。由于我们的内存使用量非常低(与之前的 Go 版本相比),因此高对象分配速度成为主要嫌疑点。

如果是这种情况,我们可以做两件事:

  • 根据我们服务的行为调整 Go GC 活动,即延迟其激活,以便减少 GC 发生的频率。 这将导致我们必须补偿更多的内存使用。
  • 查找代码中分配过多对象的函数、部分或行。

查看我们的实例类型,很明显我们有大量内存可供使用,并且受到计算机上 CPU 数量的限制。所以我们只需要调整一下比例即可。因为在Golang早期,有一个数据大多数开发者都没有关注,叫做GOGC。该值默认为 100,只是告诉您的系统何时激活 GC。当堆大小达到原始大小的两倍时,此默认值会导致 GC 触发。将此值更改为较大的数字将延迟 GC 触发并降低其频率。我们比较了许多不同的数字,最终实现我们目标的最佳性能是使用 GOGC = 2000。

这立即将 我们的内存使用量从约 200 MB 增加到约 2.7 GB (这是由于我们的 Go 版本更新,内存使用率更低),并且还 减少了大约 10% 的 CPU 使用率。

以下截图显示了这些基准测试的结果: 如何优化 Golang 服务来减少 40% 以上的 CPUGOGC =2000 个结果 — Coralogix 基准

前四个 CPU 消耗函数是我们的服务函数,非常有意义。 GC 总消耗现在约为 13%,不到之前消耗的一半!

我们实际上可以停在这里,但我们决定揭示我们分配这么多对象的位置和原因。通常有充分的理由这样做(例如,在流式传输的情况下,我们为每个检索到的消息创建许多新对象,并且必须删除它,因为它与下一条消息不相关),但在某些情况下,有一个简单的方法优化和动态减少对象创建的方法。

我们先运行和之前一样的命令,稍微修改一下,使用堆调试:

http://localhost:6060/debug/pprof/heap

要查询结果文件,您可以在代码目录中运行以下命令来分析调试结果:

go tool pprof -alloc_objects <HEAP.PROFILE.FILE>

我们的屏幕截图如下所示:

如何优化 Golang 服务来减少 40% 以上的 CPU

一切似乎都合乎逻辑,除了第三行,这是一个监视函数,它在每个 Carogix 行解析阶段结束时向 Promethes 调用者显示结果。为了获取更多信息,我们运行以下命令:

list <FunctionName>

例如:

list reportRuleExecution

然后我们得到以下结果:

如何优化 Golang 服务来减少 40% 以上的 CPU

对 WithLabelValues 的两次调用都是针对软件指标的 Prometheus 函数(我们将其留给产品)来决定是否真的有必要)。此外,我们可以看到第一行创建了大量对象(占该函数创建的所有对象的 10%)。我们进一步查了一下,发现是对导出数据绑定的consumer ID进行int到string的转换,这一点非常重要。但考虑到实际情况,我们数据库的消费者数量非常有限,不允许我们使用Prometheus方式。以字符串类型接收变量。因此,我们不是每次创建一个新字符串并在函数末尾将其丢弃(浪费分配和不必要的 GC 工作),而是在对象分配阶段定义一个映射,链接从 1 到 100,000 的所有字符串。数字和要执行的“get”方法。

现在运行另一个分析会话来验证我们的参数并确保它是正确的(您可以看到这部分不再分配对象):

如何优化 Golang 服务来减少 40% 以上的 CPU

这并不是一个显着的改进,但总的来说,它为我们节省了另一次 GC 活动,具体来说,节省了1%左右的CPU。

最终状态截图如下:

如何优化 Golang 服务来减少 40% 以上的 CPU

本文为Go语言中文网整理的GCTT翻译,发布于Go语言中文网公众号。请联系我们以获得转载许可。

最终结果

1) 内存使用率:~1.3 GB -> ~2.7 GB

2) CPU 使用率:~2.55 平均值和~5.05 峰值 -> ~2.13 平均值和~2.9 峰值。

Golang 优化之前的 CPU:

如何优化 Golang 服务来减少 40% 以上的 CPU

Golang 优化之后的 CPU:

如何优化 Golang 服务来减少 40% 以上的 CPU

一般来说,我们可以看到最重要的改进发生在高峰时段,此时日志处理每秒都会增加。这意味着我们的基础设施不仅不再需要适应异常值,而且变得更加稳定。

总结

测试 Go 解析服务的性能帮助我们识别问题区域,更好地了解我们的服务,并确定我们需要在哪里(如果在哪里)投入时间进行改进。大多数性能分析工作都会以一些基本的数值调整或配置调整结束,这些调整或配置调整更适合您的用例并最终显示出更好的性能。


来自:https://medium.com/coralogix-engineering/optimizing-a-golang-service-to-reduce-over-40-cpu-366b67c​​67ef9

作者:Eliezer Yaacov[4]译者:sh1luo[5]校对者:@unknwon[6]

本文由GCTT[7]、Go中文网原创编译 [8]荣耀上线,发布于Go语言中文网公众号。请联系我们以获得转载许可。

参考资料

[1] 官方网站:https://coralogix.com/

[2] 文档:https://docs.studygolang.com/doc/devel/release .html

[3]这里:https://golang.org/pkg/net/http/pprof

[4]Eliezer Yaacov:https://medium.com/@eliezerj8

[5]sh1luo:https://github.com/sh1luo

[6]@unknwon:https://github.com/unknwon

[7]GCTT:https://github.com/studygolang/GCTT

[8]前往中文网站:https://studygolang.com/

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门