欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

聊聊Golang性能分析工具pprof的使用

 更新時(shí)間:2023年05月25日 11:46:06   作者:洛小妍  
對(duì)于線上穩(wěn)定運(yùn)行的服務(wù)來說,?可能會(huì)遇到?cpu、mem?利用率升高的問題,那我們就需要使用?pprof?工具來進(jìn)行性能分析,所以本文就來和大家講講pprof的具體使用吧

對(duì)于線上穩(wěn)定運(yùn)行的服務(wù)來說, 可能會(huì)遇到 cpu、mem 利用率升高的問題。有時(shí)可能增加機(jī)器配置或重啟服務(wù)也解決不了問題。那我們就需要使用 pprof 工具來進(jìn)行性能分析, 找出造成 cpu、mem 升高的問題。

pprof

pprof 是 golang 的性能分析工具。pprof 通過讀取 profile.proto 格式的性能采樣數(shù)據(jù)來生成相關(guān)報(bào)告。從而分析服務(wù)中存在的性能問題。

pprof profiles

存儲(chǔ)采樣的數(shù)據(jù)集合。在 golang 中, 采樣數(shù)據(jù)分別包含這幾種類型:

cpu

cpu 采樣是最常用的采用類型。當(dāng)開始 cpu 采樣時(shí), 每隔 10ms 會(huì)進(jìn)行一次采樣, 記錄當(dāng)前運(yùn)行 goroutines 的堆棧信息。當(dāng)采樣完成之后, 可以分析哪些代碼比較消耗 cpu。

memory

memory 采樣主要用來分析服務(wù)的堆內(nèi)存的分配和回收。程序運(yùn)行時(shí), 棧內(nèi)存是可以直接分配和回收的。因此, 無需采集棧內(nèi)存的使用情況。

block

采集阻塞數(shù)據(jù)。主要用于定位 channel 所導(dǎo)致的 goroutines 阻塞。

mutex

主要用來采集鎖競爭相關(guān)數(shù)據(jù), 分析鎖競爭導(dǎo)致的系統(tǒng)的延遲。

生成 profile 文件

在開始進(jìn)行性能相關(guān)分析之前, 需要生成對(duì)應(yīng)的采樣文件。然后通過 pprof 工具對(duì) profile 文件進(jìn)行分析, 找出影響性能的代碼。在 golang 中, 可以使用以下三種方式生成 profile 文件:

測試函數(shù)

在使用基準(zhǔn)測試時(shí), 可以指定參數(shù)生成對(duì)應(yīng)的 profile 文件。

go test -cpuprofile {proflie_name} -bench .

http 訪問

對(duì)于持續(xù)運(yùn)行的任務(wù), 例如:http、rpc??梢詫?dǎo)入 net/http/pprof 來注冊 debug 路由, 生成對(duì)應(yīng)的 proflie 文件。對(duì)于非 http 的服務(wù), 需要注冊一個(gè) debug 端口。

直接生成 profile 文件

對(duì)于非持續(xù)性任務(wù)。例如:定時(shí)任務(wù)。可以通過指定方法生成對(duì)應(yīng)的 proflie 文件。

import (
   "github.com/pkg/profile" // 對(duì) runtime/pprof 進(jìn)行的封裝
   "fmt"
)
func main() {
   defer func() {
      profile.Start(profile.CPUProfile, profile.ProfilePath("."))
   }()
   func() {
      fmt.Println(5 + 6)
   }()
}

分析 profile 文件

下面將通過具體的例子, 來嘗試分析對(duì)應(yīng)的 cpu、mem 等指標(biāo)。

cpu 分析

在下面的例子中, 嘗試分析哪個(gè)函數(shù)比較占用 cpu.

package main
import (
   "log"
   "net/http"
   _ "net/http/pprof"
   "runtime"
   "time"
)
//go:noinline
func req(num int64) {
   A(num)
   B(num)
}
//go:noinline
func B(num int64) {
   for i := int64(0); i < num/2; i++ {
   }
}
//go:noinline
func A(num int64) {
   for i := int64(0); i < num; i++ {
   }
}
func main() {
   runtime.GOMAXPROCS(1)
   go func() {
      log.Println(http.ListenAndServe("localhost:3016", nil))
   }()
   for {
      time.Sleep(time.Second / 4)
      go func() {
         req(time.Now().Unix())
      }()
   }
}

下載并保存對(duì)應(yīng)的 cpu profile, 采樣時(shí)間 19s:

curl ``http://127.0.0.1:3016/debug/pprof/profile``?seconds=19 --output cpu.pprof

啟動(dòng)可視化界面:

go tool pprof -http=:8080 cpu.pprof

從最右側(cè)可以看出, 此次采集共耗時(shí) 19.21s, 采樣得到的 cpu 時(shí)間為 16.73s 「87.10%」。由于是采樣, 所以 total samples 是小于 Duration 的。通過 view 來切換不同的試圖。

Top, 用于展示不同函數(shù)的耗時(shí)占比。Flat: 當(dāng)前函數(shù)運(yùn)行耗時(shí)。Flat%: Flat 對(duì)應(yīng)的占比。Sum%: 累積使用占比。Cum: 當(dāng)前函數(shù)及子函數(shù)運(yùn)行耗時(shí)。Cum%: Cum 對(duì)應(yīng)的占比。

對(duì)于下面的例子:統(tǒng)計(jì)到的 cpu 時(shí)間為 16.73s, 函數(shù) A 本身耗時(shí) 13.3s 「79.49%」; 函數(shù) A 及其子函數(shù)耗時(shí)為 13.85s「82.78%」; 函數(shù) req 本身耗時(shí) 0; 子函數(shù)耗時(shí) 16.73s「100%」。

Graph, 以圖的形式展示不同函數(shù)的耗時(shí)占比。如下圖所示:函數(shù)入口為 main.func2「匿名函數(shù)」; 0 of 16730ms「匿名函數(shù)不占用任何資源, cpu 采樣總時(shí)長為 16730ms」; main.req 為 main.func2 的子函數(shù)「函數(shù)自身不占用任何資源」; main.A「13850ms 函數(shù)自身占用 13300ms」 和 mian.B「2880ms 函數(shù)自身占用 2810ms」 為 main.req「16730ms」 的子函數(shù)。

通過 Graph 可以直觀的看到哪些函數(shù)占用的資源比較高, 方框越大資源占比越高。同時(shí)可以直觀的看到調(diào)用關(guān)系。

Flame Graph, 火焰圖。通過火焰圖可以看到各個(gè)函數(shù)的耗時(shí), 以及子函數(shù)的耗時(shí)。橫軸越長占用的資源越多, 縱軸越長, 調(diào)用層級(jí)越深。

Source, 查看對(duì)應(yīng)的源碼。在下圖中, req 的子函數(shù) A 和 B 共占用了 100% 的資源。在函數(shù) B 中, 函數(shù)自身耗時(shí)占用了 2.81s, 其中 for 循環(huán)語句占用 2.18s。

mem 分析

下面是一段內(nèi)存分配的代碼。heap 采樣并不需要像 cpu 采樣一樣確定固定周期。heap 關(guān)注的是內(nèi)存的分配和回收, 只需采集對(duì)應(yīng)的事件即可。

package main
import (
   "fmt"
   "log"
   "net/http"
   _ "net/http/pprof"
   "runtime"
   "time"
)
//go:noinline
func req() {
   buf = append(buf, allocate()...)
}
//go:noinline
func allocate() []byte {
   var b []byte
   for i := 0; i < 1024; i++ {
      temp := make([]byte, 1024)
      b = append(b, temp...)
   }
   return b
}
var buf []byte
func main() {
   runtime.GOMAXPROCS(1)
   go func() {
      log.Println(http.ListenAndServe("localhost:3016", nil))
   }()
   for {
      time.Sleep(time.Second)
      go func() {
         req()
         fmt.Println(len(buf))
      }()
   }
}

下載堆內(nèi)存采樣文件。

curl ``http://127.0.0.1:3016/debug/pprof/heap`` --output mem.pprof

使用 webui 打開文件。

go tool pprof -http=:8080 mem.pprof

共分為四種采樣類型:alloc_objects: 所有分配的對(duì)象; alloc_space: 所有分配的空間; inuse_objects: 活躍的對(duì)象; inuse_space: 活躍的空間。這里需要注意的是, inuse_space 使用的空間是要小于當(dāng)前進(jìn)程使用的系統(tǒng)空間的「使用中的, gc 未釋放給操作系統(tǒng)的, cgo 申請(qǐng)的空間」

通過 alloc 可以分析哪里在頻繁的分配空間, inuse 可以用來分析內(nèi)存沒有釋放。切到 inuse_sapce, 可以看到 req 函數(shù)以及它的子函數(shù) allocate 在不斷分配空間。

goroutine 分析

下面是一個(gè) goroutine 泄漏的例子。

package main
import (
   "fmt"
   "log"
   "net/http"
   _ "net/http/pprof"
   "runtime"
   "time"
)
//go:noinline
func req() {
   c := make(chan struct{})
   tick := time.NewTicker(time.Millisecond)
   go func() {
      time.Sleep(time.Millisecond * 2)
      c <- struct{}{}
   }()
   select {
   case <-c:
      return
   case <-tick.C:
      return
   }
}
func main() {
   runtime.GOMAXPROCS(1)
   go func() {
      log.Println(http.ListenAndServe("localhost:3016", nil))
   }()
   for {
      time.Sleep(time.Second / 10)
      go func() {
         req()
      }()
      fmt.Println(runtime.NumGoroutine())
   }
}

下載堆 goroutine 采樣文件。

curl ``http://127.0.0.1:3016/debug/pprof/``goroutine --output goroutine.pprof

使用 webui 打開文件。

go tool pprof -http=:8080 goroutine.pprof

切到火焰圖, 可以看到 main.req.func1「匿名函數(shù)」占用了需要 goroutine。我們切到 Souce 定位到代碼行數(shù)??梢钥吹? 卡在了往非緩存的 channel 寫數(shù)據(jù)。由于接收端已經(jīng) return, 以此 goroutine 等待寫入而無法釋放。我們只需聲明一個(gè)帶緩沖的 channel 就可以解決問題。

總結(jié)

本文簡單介紹了 golang 的性能分析工具。如何進(jìn)行性能采樣, 查看采樣的數(shù)據(jù)并對(duì)其進(jìn)行分析。介紹了 Graph、Flame Graph、Source、Top 的用法。最后, 通過幾個(gè)簡單的例子來進(jìn)行 cpu、mem、goroutine 的分析。

其實(shí), 在真實(shí)的的線上服務(wù)中, 如果沒有遇到具體的問題「接口變慢、cpu 或 mem 持續(xù)升高、goroutine 泄漏、死鎖」, 不太建議過度的進(jìn)行優(yōu)化。倒不如花精力聚焦在業(yè)務(wù)思考上。

以上就是聊聊Golang性能分析工具pprof的使用的詳細(xì)內(nèi)容,更多關(guān)于Golang pprof的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • go實(shí)現(xiàn)服務(wù)優(yōu)雅關(guān)閉的示例

    go實(shí)現(xiàn)服務(wù)優(yōu)雅關(guān)閉的示例

    本文主要介紹了go實(shí)現(xiàn)服務(wù)優(yōu)雅關(guān)閉的示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-02-02
  • GoFrame框架gredis優(yōu)雅的取值和類型轉(zhuǎn)換

    GoFrame框架gredis優(yōu)雅的取值和類型轉(zhuǎn)換

    這篇文章主要為大家介紹了GoFrame框架gredis優(yōu)雅的取值和類型轉(zhuǎn)換,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06
  • Go語言封裝HTTP請(qǐng)求的Curl工具包詳解

    Go語言封裝HTTP請(qǐng)求的Curl工具包詳解

    在 Go 語言開發(fā)中,與 HTTP 服務(wù)進(jìn)行交互是非常常見的需求,本文將分享一個(gè)用 Go 語言封裝的 Curl 工具包,它提供了簡潔易用的接口來進(jìn)行 HTTP 請(qǐng)求,需要的可以了解下
    2025-03-03
  • Golang字符串類型原理及其使用方法

    Golang字符串類型原理及其使用方法

    本文主要介紹了Golang字符串類型原理及其使用方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-09-09
  • Golang?Time包與日期函數(shù)的用法詳解

    Golang?Time包與日期函數(shù)的用法詳解

    在golang中,time包提供了時(shí)間的顯示和測量用的函數(shù),下面小編就來和大家詳細(xì)聊聊Golang中Time包與日期函數(shù)的具體用法,快跟隨小編一起學(xué)習(xí)一下吧
    2023-07-07
  • Go批量操作excel導(dǎo)入到mongodb的技巧

    Go批量操作excel導(dǎo)入到mongodb的技巧

    這篇文章主要介紹了Go批量操作excel導(dǎo)入到mongo,包括選擇命令行包,讀取配置連接數(shù)據(jù)庫的方法,本文示例代碼相結(jié)合給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2022-03-03
  • Go語言實(shí)現(xiàn)Sm2加解密的示例代碼

    Go語言實(shí)現(xiàn)Sm2加解密的示例代碼

    本文主要介紹了Go語言實(shí)現(xiàn)Sm2加解密的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-03-03
  • Go語言官方依賴注入工具Wire的使用教程

    Go語言官方依賴注入工具Wire的使用教程

    依賴注入是一種實(shí)現(xiàn)控制反轉(zhuǎn)且用于解決依賴性問題的設(shè)計(jì)模式。Golang?中常用的依賴注入工具主要有?Inject?、Dig?等。但是今天主要介紹的是?Go?團(tuán)隊(duì)開發(fā)的?Wire,一個(gè)編譯期實(shí)現(xiàn)依賴注入的工具,感興趣的可以了解一下
    2022-09-09
  • GO語言 復(fù)合類型專題

    GO語言 復(fù)合類型專題

    這篇文章主要介紹了GO語言 復(fù)合類型的的相關(guān)資料,文中講解非常細(xì)致,代碼幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下
    2020-06-06
  • 使用Golang實(shí)現(xiàn)WebSocket心跳機(jī)制

    使用Golang實(shí)現(xiàn)WebSocket心跳機(jī)制

    WebSocket是一種在客戶端和服務(wù)器之間實(shí)現(xiàn)全雙工通信的協(xié)議,它允許實(shí)時(shí)地傳輸數(shù)據(jù),并且比傳統(tǒng)的HTTP請(qǐng)求更加高效,在使用Golang構(gòu)建WebSocket應(yīng)用程序時(shí),一個(gè)重要的考慮因素是如何實(shí)現(xiàn)心跳機(jī)制,所以本文將探討如何使用Golang實(shí)現(xiàn)WebSocket心跳
    2023-11-11

最新評(píng)論