转自https://www.cnblogs.com/li-peng/p/9391543.html
ソフトウェア開発プロセスでは、オンラインプロジェクトは終わりではありません。オンラインにした後は、プログラムの動作をサンプリングして分析し、既存の機能を再構築して、プログラムをより効率的かつ安定的に実行する必要があります。golangツールキットにはpprof関数が付属しており、より多くのメモリとCPUを使用するプログラムの部分を簡単に見つけることができます。ユーバーフレームグラフ、ビジュアルディスプレイと組み合わせて、プログラムをよりシンプルかつ明確に分析しましょう。
pprof有两个包用来分析程序一个是net/http/pprof另一个是runtime/pprof,net/http/pprof只是对runtime/pprof包进行封装并用http暴露出来,如下图源码所示:
net / http / pprofを使用してWebサービスを分析する
pprofはWebプロジェクトを分析します。パッケージをインポートするだけで、非常に簡単です。
_ "net/http/pprof"
小さなWebサーバーを作成する
package main
import (
_ "net/http/pprof"
"net/http"
"time"
"math/rand"
"fmt"
)
var Count int64 = 0
func main() {
go calCount()
http.HandleFunc("/test", test)
http.HandleFunc("/data", handlerData)
err := http.ListenAndServe(":9909", nil )
if err != nil {
panic(err)
}
}
func handlerData(w http.ResponseWriter, r *http.Request) {
qUrl := r.URL
fmt.Println(qUrl)
fibRev := Fib()
var fib uint64
for i:= 0; i < 5000; i++ {
fib = fibRev()
fmt.Println("fib = ", fib)
}
str := RandomStr(RandomInt(100, 500))
str = fmt.Sprintf("Fib = %d; String = %s", fib, str)
w.Write([]byte(str))
}
func test(w http.ResponseWriter, r *http.Request) {
fibRev := Fib()
var fib uint64
index := Count
arr := make([]uint64, index)
var i int64
for ; i < index; i++ {
fib = fibRev()
arr[i] = fib
fmt.Println("fib = ", fib)
}
time.Sleep(time.Millisecond * 500)
str := fmt.Sprintf("Fib = %v", arr)
w.Write([]byte(str))
}
func Fib() func() uint64 {
var x, y uint64 = 0, 1
return func() uint64 {
x, y = y, x + y
return x
}
}
var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890")
func RandomStr(num int) string {
seed := time.Now().UnixNano()
if seed <= 0 {
seed = time.Now().UnixNano()
}
rand.Seed(seed)
b := make([]rune, num)
for i := range b {
b[i] = letterRunes[rand.Intn(len(letterRunes))]
}
return string(b)
}
func RandomInt(min, max int) int {
rand.Seed(time.Now().UnixNano())
return rand.Intn(max - min + 1) + min
}
func calCount() {
timeInterval := time.Tick(time.Second)
for {
select {
case i := <- timeInterval:
Count = int64(i.Second())
}
}
}
Webサービスはポート9909を監視します。Web
サーバーには2つのhttpメソッド
テストがあります。現在の秒数に従ってFibonacci計算
データを実行します。5000のFibonacci計算を
実行し、ランダムな文字列を返します。プログラムを実行してhttpにアクセスします。 //192.168.3.34:9909/debug/pprof/プロファイル関連情報のWebバージョンを表示できます。
これらのパスは
-
/ debug / pprof / profile:このリンクにアクセスすると、30秒間のCPUプロファイリングが自動的に実行され、ダウンロード用のファイルが生成されます。
-
/ debug / pprof / block:Goroutineブロッキングイベントの記録。デフォルトでは、ブロッキングイベントが発生するたびにサンプルが取得されます。
-
/ debug / pprof / goroutines:アクティブなGoroutinesのレコード。取得時に一度だけサンプリングします。
-
/ debug / pprof / heap:ヒープメモリ割り当ての記録。デフォルトでは、512Kバイトごとに1回サンプリングします。
-
/ debug / pprof / mutex:競合ミューテックスの所有者を表示します。
-
/ debug / pprof / threadcreate:システムスレッド作成の記録。取得時に一度だけサンプリングします。
これらのgolangは、分析のためのより便利な方法を提供してくれます。コマンドを使用して詳細情報にアクセスしましょう
wrkを使用して2つの方法にアクセスするため、サービスは高速実行状態になり、サンプリング結果はより正確になります。
wrk -c 20 -t 5 -d 3m http://192.168.3.34:9909/data
wrk -c 20 -t 5 -d 3m http://192.168.3.34:9909/test
CPU使用率を分析する
コマンドを使用してCPU使用率を分析する
go tool pprof httpdemo http://192.168.3.34:9909/debug/pprof/profile
デフォルトでは、Go言語のランタイムシステムは100Hzの周波数でCPU使用率をサンプリングします。つまり、1秒あたり100サンプル、つまり10ミリ秒に1回です。なぜこの周波数を使用するのですか?100 Hzで十分なデータを生成できますが、システムを停止することはできません。また、合計サンプル数を1秒あたりのサンプル数に変換するなど、100という数値も簡単に変換できます。実際、ここで説明されているCPU使用率のサンプリングは、現在のGoroutineスタックのプログラムカウンターのサンプリングです。
デフォルトのサンプリング時間は30秒です。-secondsコマンドでサンプリング時間を指定できます。サンプリング後、コマンドライン状態になります。
ヘルプを入力して、関連するコマンドを表示できます。一般的に使用されるコマンドをいくつか示します。
-
topコマンド、input topコマンドのデフォルトは、cpuを占有する上位10のメソッドを追加することです。もちろん、コマンドの後に数字を追加することで、一番上の数字を指定できます
-
listコマンドは、規則に従って関連するメソッドを出力します。オプションのoの直後に、すべてのメソッドを出力します。次の
ようにメソッド名を指定することもできます。handlerDataメソッドはcpuの74.81%を占めます
-
Webコマンド:Webページの形式で表示:cpuの使用状況をより直感的に表示
メモリ使用量を分析する
cpuの分析と同様のコマンドを使用する
go tool pprof httpdemo http://192.168.3.34:9909/debug/pprof/heap
デフォルトでは、サンプリング時に現在のメモリ使用量のみが取得されます。オプションのコマンドalloc_objectsを追加して、プログラムの最初からメモリをサンプリングできます。
go tool pprof -alloc_objects httpdemo http://192.168.3.34:9909/debug/pprof/heap
cpuコマンドと同じ、トップリストWeb。違いは、メモリ使用量がここに表示されることです。ここでは説明しません。
go-torchをインストールします
より便利なツールはuberのゴートーチです
インストールは簡単です
go get github.com/uber/go-torch
cd $GOPATH/src/github.com/uber/go-torch
git clone https://github.com/brendangregg/FlameGraph.git
次に、FlameGraphの下のflamegraph.plを/ usr / local / binにコピーします。
フレームグラフ分析CPU
コマンドを使用する
go-torch -u http://192.168.3.34:9909 --seconds 60 -f cpu.svg
cpu.svgファイルは現在のディレクトリに生成され、ブラウザで開かれて
、アプリケーションの問題をより直感的に確認できます。handlerDataメソッドはCPU時間に時間がかかりすぎます。次に、コードに移動して分析と最適化を行います。
フレームグラフ分析メモリ
コマンドを使用する
go-torch http://192.168.3.34:9909/debug/pprof/heap --colors mem -f mem.svg
cpu.svgファイルは現在のディレクトリに生成され、ブラウザで開かれます
runtime / pprofを使用してプロジェクトを分析します
プロジェクトがrpcサービスなどのWebサービスでない場合は、runtime / pprofを使用する必要があります。彼はたくさんのメソッドを提供していて、時間があればソースコードを見ることができます。
私は簡単なツールクラスを書きました。通話分析に使用
package profapp
import (
"os"
"rrnc_im/lib/zaplogger"
"go.uber.org/zap"
"runtime/pprof"
"runtime"
)
func StartCpuProf() {
f, err := os.Create("cpu.prof")
if err != nil {
zaplogger.Error("create cpu profile file error: ", zap.Error(err))
return
}
if err := pprof.StartCPUProfile(f); err != nil {
zaplogger.Error("can not start cpu profile, error: ", zap.Error(err))
f.Close()
}
}
func StopCpuProf() {
pprof.StopCPUProfile()
}
//--------Mem
func ProfGc() {
runtime.GC() // get up-to-date statistics
}
func SaveMemProf() {
f, err := os.Create("mem.prof")
if err != nil {
zaplogger.Error("create mem profile file error: ", zap.Error(err))
return
}
if err := pprof.WriteHeapProfile(f); err != nil {
zaplogger.Error("could not write memory profile: ", zap.Error(err))
}
f.Close()
}
// goroutine block
func SaveBlockProfile() {
f, err := os.Create("block.prof")
if err != nil {
zaplogger.Error("create mem profile file error: ", zap.Error(err))
return
}
if err := pprof.Lookup("block").WriteTo(f, 0); err != nil {
zaplogger.Error("could not write block profile: ", zap.Error(err))
}
f.Close()
}
たとえば、分析が必要なメソッドでこれらのメソッドを呼び出すと、rpcでいくつかのメソッドを開きました。
type TestProf struct {
}
func (*TestProf) StartCpuProAct(context.Context, *im_test.TestRequest, *im_test.TestRequest) error {
profapp.StartCpuProf()
return nil
}
func (*TestProf) StopCpuProfAct(context.Context, *im_test.TestRequest, *im_test.TestRequest) error {
profapp.StopCpuProf()
return nil
}
func (*TestProf) ProfGcAct(context.Context, *im_test.TestRequest, *im_test.TestRequest) error {
profapp.ProfGc()
return nil
}
func (*TestProf) SaveMemAct(context.Context, *im_test.TestRequest, *im_test.TestRequest) error {
profapp.SaveMemProf()
return nil
}
func (*TestProf) SaveBlockProfileAct(context.Context, *im_test.TestRequest, *im_test.TestRequest) error {
profapp.SaveBlockProfile()
return nil
}
転送
profTest.StartCpuProAct(context.TODO(), &im_test.TestRequest{})
time.Sleep(time.Second * 30)
profTest.StopCpuProfAct(context.TODO(), &im_test.TestRequest{})
profTest.SaveMemAct(context.TODO(), &im_test.TestRequest{})
profTest.SaveBlockProfileAct(context.TODO(), &im_test.TestRequest{})
考え方は同じで、プロファイルファイルは現在のフォルダにエクスポートされます。次に、フレームグラフを使用して分析します。ドメイン名は指定できません。ファイルを指定する必要があります。
go-torch httpdemo cpu.prof
go-torch httpdemo mem.prof