GCTT:sh1luo Go語言中文網(wǎng)
在 Coralogix(Go語言中文網(wǎng)譯注:一個提供全面日志分析的服務(wù)產(chǎn)品,官網(wǎng)[1]),我們?yōu)榱巳ソo我們的客戶提供關(guān)于他們?nèi)罩緦崟r的分析、警報和元數(shù)據(jù),要去解析他們的日志。在解析階段,我們需要非常快速地解析包含多個復(fù)雜規(guī)則的服務(wù)日志,這個目標(biāo)是促使我們決定使用 Golang 的原因之一。
這項新的服務(wù)現(xiàn)在就全天候的跑在生產(chǎn)階段,盡管我們看到了非常好的結(jié)果,但是它也需要跑在高性能的機(jī)器上。這項 Go 的服務(wù)跑在一臺 AWS m4.2xlarge 實例上 ,帶有 8 CPUs 和 36 GB 的配置,每天要解析幾十億的日志。
在這個階段一切都運(yùn)行正常,我們本可以自我感覺良好,但是那并不是我們在 Coralogix 想要的表現(xiàn)。我們想要更多的特性,比如性能等等,或者使用更少的 AWS 實例。為了改進(jìn),我們首先需要理解瓶頸的本質(zhì)以及我們?nèi)绾文軌驕p少或者完全解決這些問題。
我們決定在我們的服務(wù)上進(jìn)行一些分析,檢查一下到底是什么造成了 CPU 的高消耗,看看我們是否能夠優(yōu)化。
首先,我們將 Go 升級到最新的穩(wěn)定版本(這是軟件生命周期中的關(guān)鍵一步)。我們是用的 Go 1.12.4 版本,最新的是 1.13.8。根據(jù) 文檔[2] ,Go 1.13 發(fā)行版在運(yùn)行時庫方面和一些其他主要利用內(nèi)存使用的組件方面已經(jīng)有了長足的進(jìn)步??傊?,使用最新的穩(wěn)定版本能幫助我們節(jié)省許多工作。
因此,內(nèi)存消耗由大約 800 MB 降低到了僅 180 MB。
第二,為了更好的理解我們的流程以及弄清楚我們應(yīng)該在哪花費時間和資源,我們開始去進(jìn)行分析。
分析不同的服務(wù)和程序語言可能看起來很復(fù)雜并且令人望而生畏,但是對于 Go 來說它實際上十分容易,僅僅幾個命令就能夠描述清楚。Go 有一個專門的工具叫“pprof”,它通過監(jiān)聽一個路由(默認(rèn)端口 6060)能夠應(yīng)用在你的 app 上,并且使用 Go 的包來管理 HTTP 連接:
import _ "net/http/pprof"
接著在你的 main 函數(shù)中或者路由包下按照如下操作初始化:
go func() {
log.Println(http.ListenAndServe("localhost:6060", nil))
}()
現(xiàn)在你可以啟動你的服務(wù)并且連接到:
http://localhost:6060/debug/pprof
Go 官方提供的完整文檔可以 在這[3] 找到。
pprof 的默認(rèn)配置是每 30 秒對 CPU 的使用情況進(jìn)行采樣。有許多不同的選擇,也可以對 CPU 的使用,堆的使用或者其他更多的使用情況進(jìn)行采樣。
我們主要關(guān)注 CPU 使用,因此在生產(chǎn)階段采取了一個 30 秒的性能分析,并且發(fā)現(xiàn)了你在下圖所看到的情況(提醒一下:這是在我們把 Go 版本升級并且將 Go 的內(nèi)部組件降到最低之后的結(jié)果):
正如你所看到的,我們發(fā)現(xiàn)了許多運(yùn)行時庫的活動:GC 幾乎使用了 29% 的 CPU(還僅僅只是消耗最多的前 20 個對象)。因為 Go 的 GC 非常快并且做了巨大的優(yōu)化,最好的實踐就是不要去改變或者修改它。因為我們的內(nèi)存消耗非常低(與我們先前的 Go 版本相比),所以主要的懷疑對象就變成了較高的對象分配率。
如果是那種情況的話,我們就能做兩件事情了:
觀察一下我們的實例類型,很明顯我們有大量的內(nèi)存可供使用,并且我們正在被機(jī)器的 CPU 數(shù)量所限制。因此我們僅僅需要調(diào)整一下比率。因為在 Golang 的早期有一個大多數(shù)開發(fā)者都不關(guān)注的數(shù)據(jù),叫 GOGC。這個數(shù)值默認(rèn)是 100,簡單地告訴你的系統(tǒng)什么時候觸發(fā) GC。這個默認(rèn)值使得堆的大小在到達(dá)它初始態(tài)的兩倍時觸發(fā) GC。將這個數(shù)值改成一個更大的數(shù)將會延緩 GC 的觸發(fā),降低它的頻率。我們基準(zhǔn)測試了許多不同的數(shù),最終對于我們的目標(biāo)來說最好的性能是在使用 GOGC = 2000 的時候。
這立刻增加了我們的內(nèi)存使用,從大約 200 MB 到 大約 2.7 GB(那還是由于我們的 Go 版本更新,在內(nèi)存消耗降低的情況下),另外也減少了我們 CPU 大約 10% 的使用。
這個接下來的截圖就展示了這些基準(zhǔn)測試的結(jié)果:
前面的四個 CPU 的消耗函數(shù)就是我們的服務(wù)函數(shù),這十分有意義。全部的 GC 使用現(xiàn)在大約是 13%,是先前消耗的一半還少!
我們其實可以在這就停下來了,但是我們還是決定去揭露我們在哪并且為什么會分配這么多對象。很多時候,這么做有充分理由(比如在流式處理的情況下,我們?yōu)槊織l獲取的消息創(chuàng)建了許多新的對象,并且因為它與下一條消息無關(guān),需要去移除它),但是在某些情況下有一種簡單的方法可以去優(yōu)化并且動態(tài)地減少對象的創(chuàng)建。
首先,讓我們運(yùn)行一個和之前同樣的命令,有一點小的改變,采用堆調(diào)試:
http://localhost:6060/debug/pprof/heap
為了查詢結(jié)果文件,你可以運(yùn)行如下命令在你的代碼目錄下來分析調(diào)試結(jié)果:
go tool pprof -alloc_objects <HEAP.PROFILE.FILE>
我們的截圖看起來像這樣:
除了第三行一切似乎都很合理,這是一個監(jiān)控函數(shù),在每個 Carologix 規(guī)則解析階段的末尾向我們的 Promethes 調(diào)用者展示結(jié)果。為了獲取進(jìn)一步信息,我們運(yùn)行如下命令:
list <FunctionName>
例如:
list reportRuleExecution
然后我們會獲得如下結(jié)果:
WithLabelValues 的兩個調(diào)用都是為了軟件度量的 Prometheus 函數(shù)(我們將這個留給產(chǎn)品去決定是否真正需要)。而且,我們可以看到第一行創(chuàng)建了大量的對象(由這個函數(shù)所創(chuàng)建的全部對象的 10%)。我們進(jìn)一步查看發(fā)現(xiàn)它是一個對于綁定到導(dǎo)出數(shù)據(jù)的消費者 ID 從 int 到 string 的轉(zhuǎn)換,十分重要,但是考慮到實際情況,我們數(shù)據(jù)庫中消費者的數(shù)量十分有限,我們不應(yīng)該采用 Prometheus 的方式來接收變量作為 string 類型。因此取代了每次創(chuàng)建一個新的 string 并且在函數(shù)末尾都拋棄的這種方法(浪費分配還有 GC 的多余工作),我們在對象的分配階段定義了 map,配對了所有從 1 到 10 萬的數(shù)字和一個需要執(zhí)行的 “get” 方法。
現(xiàn)在運(yùn)行一個新的性能分析會話來驗證我們的論點并且它的對的(你可以看到這一部分并不會再分配對象了):
這并不是一個顯著的改進(jìn),但是總體來說為我們節(jié)省了另一個 GC 的活動,說的更具體一點就是節(jié)省了大約 1% 的 CPU。
最終的狀態(tài)就是下面的截圖:
本文是 Go語言中文網(wǎng)組織的 GCTT 翻譯,發(fā)布在 Go語言中文網(wǎng)公眾號,轉(zhuǎn)載請聯(lián)系我們授權(quán)。
1) 內(nèi)存使用:大約 1.3 GB -> 大約 2.7 GB
2) CPU 使用:大約 2.55 avg 和 大約 5.05 峰值期 -> 大約 2.13 avg 和 大約 2.9 峰值期。
在我們 Golang 優(yōu)化前的 CPU:
在我們 Golang 優(yōu)化后的 CPU:
總體來說,我們可以看到主要的改進(jìn)是在每秒日志處理量增加時的高峰時間。這就意味著我們的基礎(chǔ)架構(gòu)不僅不需要再為了異常值進(jìn)行調(diào)整,而且變得更加穩(wěn)定了。
通過對我們的 Go 解析服務(wù)進(jìn)行性能測試,我們能夠查明有問題的地方,更好的理解我們的服務(wù)并且確定在哪里(如果有的話)投資時間進(jìn)行改進(jìn)。大多數(shù)性能分析工作都會以一些基礎(chǔ)數(shù)值或配置的調(diào)整,更合適你的使用情況并且最終展現(xiàn)更好的性能而結(jié)束。
via:https://medium.com/coralogix-engineering/optimizing-a-golang-service-to-reduce-over-40-cpu-366b67c67ef9
作者:Eliezer Yaacov[4]譯者:sh1luo[5]校對:@unknwon[6]
本文由 GCTT[7] 原創(chuàng)編譯,Go 中文網(wǎng)[8] 榮譽(yù)推出,發(fā)布在 Go語言中文網(wǎng)公眾號。
官網(wǎng): 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]Go 中文網(wǎng): https://studygolang.com/
聯(lián)系客服