首頁 > 後端開發 > Golang > 主體

golang效能診斷看這篇就夠了

發布: 2023-07-24 16:18:33
轉載
1741 人瀏覽過
我們日常接觸效能診斷問題,一般分為兩種情況,一是線上應用真的出現效能問題;二是我們需要對準備上線的系統進行效能預估;後者需要壓力測試輔助進行,此處不表。
針對GO應用,效能診斷工具主要分為兩層:OS層面與GO應用層面(go tool pprof /trace /gc)


OS診斷



系統診斷,我們大致上主要關註三個面向: CPU 、Memory、I/O

1.1 CPU

golang效能診斷看這篇就夠了


  1. #CPU診斷主要關注平均負載(Load Average),CPU使用率,上下文切換(Context Switch)。常用
  2. top

    指令查看cpu使用率以及伺服器負載狀況。

  3. 平均負載:0.14 0.07 0.06 分別表示過去1分鐘、5分鐘、15分鐘的機器負載的平均值,根據經驗,若負載數值小於0.7*CPU個數則正常,超過或達到CPU核數的四、五倍,則系統的負載就明顯偏高。

    CPU的上下文切換情況可###透過#########vmstat#########指令可以查看,上下文切換發生的場景如下幾種:##################時間片用完,CPU正常調度下一個任務##################被其他優先順序較高的任務搶佔##################執行任務碰到I/O阻塞,掛起目前任務,切換到下一個任務###### #############使用者程式碼主動掛起目前任務讓出CPU###################多任務搶佔資源,因為沒搶到而被掛起######
  4. 硬體中斷

#1.2  Memory

從作業系統角度,記憶體關注應用程式是否足夠,可以使用free –m 指令查看記憶體的使用情況。

透過top 指令可以查看進程使用的虛擬記憶體VIRT 和實體記憶體RES,根據公式VIRT = SWAP RES 可以推算出具體應用使用的交換分區(Swap)情況,使用交換分區過大會影響應用效能,可以將swappiness 值調到盡可能小。


1.3 I/O

I/O 包含磁碟I/ O 和網路I/O,一般情況下磁碟更容易出現I/O 瓶頸。透過iostat可以查看磁碟的讀寫情況,透過 CPU 的 I/O wait 可以看出磁碟 I/O 是否正常。

如果磁碟 I/O 一直處於很高的狀態,表示磁碟太慢或故障,成為了效能瓶頸,需要進行應用最佳化或磁碟更換。

除了常用的top、 ps、vmstat、iostat 等指令,還有其他Linux 工具可以診斷系統問題,如mpstat、tcpdump、netstat、pidstat、sar 等更多Linux效能診斷工具如下圖:

golang效能診斷看這篇就夠了

GO應用診斷



  • ####go生態已經為我們提供了大量的API和診斷工具來幫助我們解決go應用的效能問題。我們常用的大致可以分為兩類:###############Profiling 收集程式執行過程中的具體事件,並抽樣統計方便精確定位問題######## ####Tracing 一種偵測程式碼的方法,用於分析呼叫或使用者請求整個生命週期中的延遲,且可以跨越多個Go進程。 ###

2.1 profiling

profile一般稱為效能分析,對程式而言,就是程式運行時的各種概況信息,包括cpu佔用情況、記憶體狀況、執行緒情況等。方便分析昂貴或頻繁呼叫的程式場景。
如何使用?

1.首先profiling程式碼埋入

#
import _ "net/http/pprof"


func main() {
    go func() {
        log.Println(http.ListenAndServe("0.0.0.0:9090", nil))
    }()
    ...
}
登入後複製


##2.保存特定時間點的profile,例如儲存heap資訊

curl http://localhost:6060/debug/pprof/heap --output heap.tar.gz
登入後複製

3.使用go tool pprof 分析保存的profile快照,如分析上述heap資訊

go tool pprof heap.tar.gz
登入後複製


2.1. 1  CPU Profiling

pprof可以幫忙我們分析函數執行緩慢問題 

 C#PU佔用過高問題

go tool pprof http://localhost:6060/debug/pprof/profile?second=10
登入後複製
命令列方式: 常用指令top  list traces

top: 查看依照佔用記憶體或cpu多少排序的前10的函數資訊

  • flat:目前函數所佔用的CPU時長(

    不包含其呼叫的其他函數

  • flat% :目前函數使用CPU佔總CPU時長的百分比

  • sum%:前面每一行flat百分比的和

  • ##cum: 累計量,當前函數及其子函數佔用CPU時間長度 
  • cum%:累計量佔總量的百分比

cum>=flat

list: 查看某个函数的代码 以及该函数每行代码的指标信息

traces:打印所有函数调用栈 以及调用栈的指标信息

golang效能診斷看這篇就夠了

UI界面方式:从服务器download下生成的sample文件

go tool pprof -http=:8080 pprof.xxx.samples.cpu.001.pb.gz
登入後複製

golang效能診斷看這篇就夠了


golang效能診斷看這篇就夠了

Flame graph很清晰得可以看到当前CPU被哪些函数执行栈占用


1.2 Heap Profiling

go tool pprof http://localhost:6060/debug/pprof/heap?second=10
登入後複製

命令行 UI查看方式 同理

golang效能診斷看這篇就夠了


golang效能診斷看這篇就夠了



graph中方框越大 占用内存越多 火焰图 宽度越大 占用内存越多

SAMPLE->inuse_objects可以查看当前的对象数量 这个参数对于分析gc线程占用较高cpu时很有用处 它侧重查看对象数量

inuse_space图可以查看具体的内存占用

毕竟对于10个100m的对象和1亿个10字节的对象占用内存几乎一样大,但是回收起来一亿个小对象肯定比10个大对象要慢很多。

go tool pprof -inuse_space http://localhost:6060/debug/pprof/heap : 分析应用程序的常驻内存占用情况 (默认)
go tool pprof -alloc_objects http://localhost:6060/debug/pprof/heap: 分析应用程序的内存临时分配情况
登入後複製


1.3 并发请求问题 查看方式跟上面类似。

go tool pprof http://localhost:6060/debug/pprof/goroutine
go tool pprof http://localhost:6060/debug/pprof/block
go tool pprof http://localhost:6060/debug/pprof/mutex
登入後複製


2.2 tracing

trace并不是万能的,它更侧重于记录分析 采样时间内运行时系统具体干了什么。

收集trace数据的三种方式:

1. 使用runtime/trace包 调用trace.Start()和trace.Stop()

2. 使用go test -trace=测试标识

3. 使用debug/pprof/trace handler 获取运行时系统最好的方法

例如,通过

go tool pprof http://localhost:6060/debug/pprof/trace?seconds=20 > trace.out
登入後複製

获取运行时服务的trace信息,使用

go tool trace trace.out
登入後複製

会自动打开浏览器展示出UI界面

golang效能診斷看這篇就夠了

其中trace view 只能使用chrome浏览器查看,这里go截止1.14版本存在一个 bug,解决办法如下:

go tool trace trace.out 无法查看trace view
go bug:https://github.com/golang/go/issues/25151
mac 解决版本:安装gotip
go get golang.org/dl/gotip
gotip download
then  使用 gotip tool trace trace.out即可
登入後複製

获取的trace.out 二进制文件也可以转化为pprof格式的文件

go tool trace -pprof=TYPE trace.out > TYPE.pprof
Tips:生成的profile文件 支持 network profiling、synchronization profiling、syscall profiling、scheduler profiling
go tool pprof TYPE.pprof
登入後複製


使用gotip tool trace trace.out可以查看到trace view的丰富操作界面:

操作技巧:

ctrl + 1 选择信息

ctrl + 2 移动选区

ctrl + 3 放大选区

ctrl + 4 指定选区区间

shift + ? 帮助信息

AWSD跟游戏快捷键类似 玩起来跟顺手

整体的控制台信息 如下图:

golang效能診斷看這篇就夠了


  • 时间线: 显示执行的时间单元 根据时间的纬度不同 可以调整区间

  • 堆: 显示执行期间内存的分配和释放情况

  • 协程(Goroutine): 显示每个时间点哪些Goroutine在运行 哪些goroutine等待调度 ,其包含 GC 等待(GCWaiting)、可运行(Runnable)、运行中(Running)这三种状态。

goroutine区域选中时间区间

golang效能診斷看這篇就夠了

OS线程(Machine): 显示在执行期间有多少个线程在运行,其包含正在调用 Syscall(InSyscall)、运行中(Running)这两种状态。

golang效能診斷看這篇就夠了

  • 虚拟处理器Processor: 每个虚拟处理器显示一行,虚拟处理器的数量一般默认为系统内核数。数量由环境变量GOMAXPROCS控制

  • 协程和事件: 显示在每个虚拟处理器上有什么 Goroutine 正在运行,而连线行为代表事件关联。

每个Processor分两层,上一层表示Processor上运行的goroutine的信息,下一层表示processor附加的事件比如SysCall 或runtime system events

golang效能診斷看這篇就夠了

ctrl+3 放大选区,选中goroutine 可以查看,特定时间点 特定goroutine的执行堆栈信息以及关联的事件信息

golang效能診斷看這篇就夠了

goroutine analysis

golang效能診斷看這篇就夠了

点击goroutine的id 可以跳到trace view 详细查看goroutine具体干了什么

名称
含义
Execution执行时间
Network wait网络等待时间
Sync Block同步阻塞时间
Blocking syscall系统调用阻塞时间
Scheduler wait调度等待时间
GC SweepingGC清扫时间
GC PauseGC暂停时间


实践 一个延迟问题诊断

当我们一个执行关键任务的协程从运行中被阻塞。这里可能的原因:被syscall阻塞 、阻塞在共享内存(channel/mutex etc)、阻塞在运行时(如 GC)、甚至有可能是运行时调度器不工作导致的。这种问题使用pprof很难排查,

使用trace只要我们确定了时间范围就可以在proc区域很容易找到问题的源头

golang效能診斷看這篇就夠了

上图可见,GC 的MARK阶段阻塞了主协程的运行



2.3 GC

golang的gc算法是根据标记清除改进的三色标记法,大概流程:

初始所有对象都是白色

  1. Stack scan阶段:从root出发扫描所有可达对象,标记为灰色并放入待处理队列;root包括全局指针和goroutine栈上的指针

  2. Mark阶段:1.从待处理队列取出灰色对象,将其引用的对象标记为灰色并放入队列,自身标记为黑色 2. re-scan全局指针和栈,因为mark和用户程序并行运行,故过程1的时候可能会有新的对象分配,这时需要通过写屏障(write barrier)记录下来;re-scan再完成检查;

  3. 重复步骤Mark阶段,直到灰色对象队列为空,执行清扫工作(白色即为垃圾对象)

go的三色标记法也存在STW(Stop The World),大致有两个场景
  1. GC即将开始时,需要STW 做一些准备工作, 如enable write barrier

  2. re-scan也需要STW,否则上面Mark阶段的re-scan无法终止

通过GODEBUG=gctrace=1可以开启gc日志,查看gc的结果信息

$ GODEBUG=gctrace=1 go run main.go   
gc 1 @0.001s 19%: 0.014+3.7+0.015 ms clock, 0.11+2.8/5.7/3.2+0.12 ms cpu, 5->6->6 MB, 6 MB goal, 8 P
gc 2 @0.024s 6%: 0.004+3.4+0.010 ms clock, 0.032+1.4/4.5/5.3+0.085 ms cpu, 13->14->13 MB, 14 MB goal, 8 P
gc 3 @0.093s 3%: 0.004+6.1+0.027 ms clock, 0.032+0.19/11/15+0.22 ms cpu, 24->25->22 MB, 26 MB goal, 8 P
scvg: 0 MB released
scvg: inuse: 4, idle: 58, sys: 63, released: 58, consumed: 4 (MB)
scvg: 0 MB released
scvg: inuse: 4, idle: 58, sys: 63, released: 58, consumed: 4 (MB)
scvg: 0 MB released
scvg: inuse: 4, idle: 58, sys: 63, released: 58, consumed: 4 (MB)
scvg: 0 MB released
scvg: inuse: 4, idle: 58, sys: 63, released: 58, consumed: 4 (MB)
登入後複製


格式

gc # @#s #%: #+#+# ms clock, #+#/#/#+# ms cpu, #->#-># MB, # MB goal, # P

含义

gc#:GC 执行次数的编号,每次叠加。

@#s:自程序启动后到当前的具体秒数。

#%:自程序启动以来在GC中花费的时间百分比。

#+...+#:GC 的标记工作共使用的 CPU 时间占总 CPU 时间的百分比。

#->#-># MB:分别表示 GC 启动时, GC 结束时, GC 活动时的堆大小.

#MB goal:下一次触发 GC 的内存占用阈值。

#P:当前使用的处理器 P 的数量。



拓展


当我们的程序陷入CPU 和IO混和负载过高时,我们使用pprof profile只能检测出CPU耗时的函数,但是屏蔽了IO等待过长的函数。

https://github.com/felixge/fgprof 给出了一个解决方案:

具体做法是:用一个后台协程在采样时间区间内每秒99次调用runtime.GoruntineProfile,返回的结果忽略了协程当时消耗CPU还是非消耗CPU的区别 进行统计,保存在内存中的map中,可导出转化为pprof

具体用法:

package main
 
import(
    _ "net/http/pprof"
    "github.com/felixge/fgprof"
)
 
func main() {
    http.DefaultServeMux.Handle("/debug/fgprof", fgprof.Handler())
    go func() {
        log.Println(http.ListenAndServe(":6060", nil))
    }()
 
    // <code to profile>
}
 
git clone https://github.com/brendangregg/FlameGraph
cd FlameGraph
curl -s &#39;localhost:6060/debug/fgprof?seconds=3&#39; > fgprof.fold
./flamegraph.pl fgprof.fold > fgprof.svg
登入後複製

如果遇到这种CPU消耗型和非CPU消耗型混合的情况下 可以试试排查下。

以上是golang效能診斷看這篇就夠了的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:Go语言进阶学习
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
最新問題
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板