golang 效能調優分析工具 pprof(下)

九卷發表於2021-03-30

golang 效能調優分析工具 pprof(上)篇, 這是下篇。

四、net/http/pprof

4.1 程式碼例子 1

go version go1.13.9

把上面的程式例子稍微改動下,命名為 demohttp.go:

package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"log"
	"math/rand"
	"net/http"
	_ "net/http/pprof"
	"sync"
)

func main() {
	http.HandleFunc("/pprof-test", handler)

	fmt.Println("http server start")
	err := http.ListenAndServe(":8090", nil)
	if err != nil {
		log.Fatal(err)
	}
}

func handler(resp http.ResponseWriter, req *http.Request) {
	var wg sync.WaitGroup
	wg.Add(200)

	for i := 0; i < 200; i++ {
		go cyclenum(30000, &wg)
	}

	wg.Wait()

	wb := writeBytes()
	b, err := ioutil.ReadAll(wb)
	if err != nil {
		resp.Write([]byte(err.Error()))
		return
	}
	resp.Write(b)
}

func cyclenum(num int, wg *sync.WaitGroup) {
	slice := make([]int, 0)
	for i := 0; i < num; i++ {
		for j := 0; j < num; j++ {
			j = i + j
			slice = append(slice, j)
		}
	}
	wg.Done()
}

func writeBytes() *bytes.Buffer {
	var buff bytes.Buffer

	for i := 0; i < 30000; i++ {
		buff.Write([]byte{'a' + byte(rand.Intn(10))})
	}
	return &buff
}

4.2 開始分析

4.2.1 在 web 介面上分析

先執行上面的 demohttp.go 程式,執行命令:

go run demohttp.go

然後在瀏覽器輸入:http://localhost:8090/debug/pprof/,檢視服務執行情況,如下圖:

名稱 url 說明
allocs $host/debug/pprof/allocs?debug=1 過去所有記憶體抽樣情況
block $host/debug/pprof/block?debug=1 同步阻塞時程式棧跟蹤的一些情況
heap $host/debug/pprof/heap?debug=1 活動物件的記憶體分配情況
mutex $host/debug/pprof/mutex?debug=1 互斥鎖持有者的棧幀情況
profile $host/debug/pprof/profile cpu profile,點選時會得到一個檔案,然後可以用 go tool pprof 命令進行分析
threadcreate $host/debug/pprof/threadcreate?debug=1 建立新 OS 執行緒的堆疊跟蹤情況
trace $host/debug/pprof/trace 當前程式執行的追蹤情況,點選時會得到一個檔案,可以用 go tool trace 命令來分析這個檔案

點選上面的連結,就可以檢視具體的分析情況。
不斷重新整理網頁,可以看到資料在不斷變化。

4.2.2 命令列互動分析

在命令列上執行 demohttp.go 程式,執行命令:

go run demohttp.go

A. 分析 cpu profile

在開啟另外一個命令列終端,執行如下命令:

go tool pprof http://localhost:8090/debug/pprof/profile?seconds=70

引數 seconds = 70:進行 70s 的資料樣本採集,這個引數可以根據實際情況調整。

上面的命令執行後,會等待 70s , 然後才會進入命令互動介面,如上圖

輸入 top 命令:

大家發現沒,其實與上面 runtime/pprof 在命令列互動時是一樣的操作,可以參考上面的欄位引數說明。

找出耗時程式碼部分,也可以用命令:list

top 命令執行後,發現什麼問題沒?這個 top 命令顯示的資訊都是系統呼叫資訊耗時,沒有使用者定義的函式。為什麼?下面進行分析。

B. 分析 memory profile

執行命令:

go tool pprof http://localhost:8090/debug/pprof/heap

然後同樣輸入 top 命令檢視函式使用情況,如下圖:

其餘的跟蹤分析命令類似,就不一一分析了。

把上面在終端命令列下互動分析的資料進行視覺化分析。

4.2.3 圖形視覺化分析

A. pprof 圖形視覺化

在前面視覺化分析中,我們瞭解到視覺化最重要有 2 步:1.採集資料 2.圖形化採集的資料。

在上面第三節 runtime/pprof 中,進入終端命令列互動操作,然後輸入 web 命令,就可以生成一張 svg 格式的圖片,用瀏覽器可以直接檢視該圖片。我們用同樣的方法來試一試。

  1. 輸入命令:

go tool pprof http://localhost:8090/debug/pprof/profile?seconds=30

  1. 等待 30s 後輸入 web 命令

如下圖:

果然生成了一個 svg 檔案,在瀏覽器檢視該圖片檔案,啥有用資訊也沒有,如下圖:

為什麼沒有有用資訊?前面有講到過,沒有使用者訪問 http server ,需要的程式沒有執行,一直阻塞在那裡等待客戶端的訪問連線,所以 go tool pprof 只能採集部分程式碼執行的資訊,而這部分程式碼又沒有消耗多少 cpu。

那怎麼辦?

一個方法就是用 http 測試工具模擬使用者訪問。這裡用 https://github.com/rakyll/hey 這個工具。
安裝 hey:

go get -u github.com/rakyll/hey

安裝完成後,進行 http 測試:

hey -n 1000 http://localhost:8090/pprof-test

同時開啟另一終端執行命令:

go tool pprof http://localhost:8090/debug/pprof/profile?seconds=120

等待 120s 後,採集資訊完成,如下圖:

輸入 top 命令檢視統計資訊:

可以看到使用者定義的一個最耗時函式是:main.cyclenum。如果要檢視這個函式最耗時部分程式碼,可以用 list cyclenum 命令檢視。

我們這裡是要生成一張圖片,所以輸入 web 命令生成圖片:

在瀏覽器上檢視 svg 圖片:

(圖片較大,只擷取了部分)

這張圖完整的展示了 top 命令的資訊。

B. web 視覺化

執行命令:

go tool pprof -http=":8080" http://localhost:8090/debug/pprof/profile

同時開啟另一終端執行測試命令:

hey -n 200 -q 5 http://localhost:8090/pprof-test

上面 go tool pprof 執行完成後,會自動在瀏覽器開啟一個 http 地址,http://localhost:8080/ui/,如下圖:


(擷取部分圖片)

這樣就可以在web瀏覽器上檢視分析資料了。

C. 火焰圖

用 http 測試框架 hey 訪問,命令為:

hey -n 200 -q 5 http://localhost:8090/pprof-test

在壓測的同時開啟另一終端執行命令:

go-torch -u http://localhost:8090

來生成火焰圖。

執行命令時在終端輸出了資訊 :

Run pprof command: go tool pprof -raw -seconds 30 http://localhost:8090/debug/pprof/profile

可以看到 go-torch 的原始命令也是用到了 go tool pprof

上面這個命令預設生成了 torch.svg 的火焰圖檔案,如下:


(擷取一部分圖展示)

點選方塊可以檢視更詳細資訊:

參考

相關文章