Coralogix工程师通过优化Go服务成功地将CPU使用率降低了40%。10年前,Google遇到了C++编译时间长导致的严重瓶颈,他们需要一个新的解决方案。为了应对这一挑战,谷歌工程师创建了一种名为Go(又名Golang)的新编程语言。Go语言借鉴了C++的优点(如性能和安全特性),同时结合了Python的开发速度,使其能够快速使用多核实现并发计算。在Coralogix,我们解析客户日志,为他们提供相应的实时分析、警报、元数据等。为此,解析阶段必须非常快,但解析阶段非常复杂,需要为每一行日志服务大量规则。这是我们认为我们正在采用Golang的原因之一。这项新服务在我们的生产环境中全天候24/7运行。虽然结果很不错,但它仍然需要在高性能机器上运行。这个Go服务运行在AWSm4.2xlarge实例上,拥有8核CPU和36GB内存,每天解析超过数百亿条日志。在这个阶段,一切都在正常运行,我们可以凑合,但这不是我们的风格。我们希望使用更少的AWS实例来提供更多的功能,比如性能。为此,我们需要了解瓶颈的性质以及如何减少或完全消除瓶颈。1.问题分析我们决定对服务进行一些分析,检查是什么导致了高CPU消耗,看看我们是否可以做一些优化工作。升级版本首先,我们将GO升级到最新的稳定版本(软件生命周期中的关键步骤)。之前,我们使用的是1.12.4版,现在是1.13.8版。根据官方文档,1.13版本对运行时库和一些对内存使用有较大影响的组件进行了重大改进。无论如何,使用最新的稳定版本是有道理的,并且为我们节省了很多工作。https://golang.org/doc/devel/release.html因此内存消耗也从800MB左右优化到了180MB左右。分析开始接下来,为了更好地了解我们的工作流程并了解我们将时间和资源花在哪里,我们从分析开始。分析不同的服务和编程语言可能看起来非常复杂和令人生畏,但在Go中,它实际上非常简单,只需几条命令即可实现。Go有一个特殊的工具叫做“pprof”,可以在应用中通过监听路由(默认端口6060)来启用,使用Go包来管理HTTP连接import_"net/http/pprof"然后,在mainfunction或在路由包中启用以下操作:gofunc(){log.Println(http.ListenAndServe("localhost:6060",nil))}()现在我们可以启动服务并连接到Http://localhost:6060/debug/可以在此处找到pprof的完整Go文档。https://golang.org/pkg/net/http/pprofpprof默认配置为每30秒采样一次CPU使用率。我们可以调整一些配置来采样参数,例如CPU使用率和堆使用率。我们主要关心的是CPU使用率,因此在生产中我们每隔30秒进行一次性能采样,看看下图显示的内容(注意:这是在我们升级Go版本并在最小化组件后更改Go的内部结果之后):可以看到,我们发现了很多和runtimelibrary(runtimepackage)相关的activity,需要指出的是GC(garbagecollection):几乎29%的CPU都被GC占用了,而这只是top20个消耗最多的对象。由于Go的GC已经非常快并且经过了大量优化,因此最好不要更改或修改它。由于我们的内存消耗非常低(与之前的Go版本相比),主要问题变成了高对象分配率。如果是这种情况,我们可以做两件事:调整GoGC活动以适应我们的服务行为,即我们需要延迟GC的触发以降低运行频率。作为代价,我们将不得不消耗更多的内存。查找分配过多对象的函数、区域或代码行。查看实例类型,我们有很多空闲内存,CPU数量受机器类型限制。所以我们需要调整这个比例。从Golang的早期开始,就有一个被大多数开发者忽略的标志:GOGC。这个阈值的默认值为100,它的主要作用是告诉系统什么时候触发GC。当堆达到其初始大小的100%时,默认将触发GC过程。将默认值更改为更高的数字会延迟GC触发,相反会更快地触发GC。我们开始针对不同的值进行基准测试,最终发现我们在GOGC=2000时获得了最好的性能。这立即将我们的内存使用量从200MB增加到2.7GB(这是在我们更新Go版本以减少内存消耗之后)并将我们的CPU使用率降低了10%。下面的截图显示了这些基准测试的结果:Gogc=2000前4个CPU使用函数成为我们的服务函数,这是有道理的。总GC使用率现在约为13%,不到之前的一半。深入挖掘我们本可以就此打住,但我们决定继续调查分配这么多对象的位置和原因。很多时候分配对象是有充分理由的(例如,在流处理的情况下,我们为每条消息创建很多新对象,因为它与下一条消息无关,需要删除),但在某些情况下,有一种简单的方法可以优化并大大减少对象创建。首先,让我们运行与之前相同的命令,有一个小的变化,使用堆转储:http://localhost:6060/debug/pprof/heap要查询生成的文件,我们可以在代码文件目录中运行以下命令分析调试结果:gotoolpprof-allocobjects
