日記に行く-パフォーマンス分析にpprofとgo-torchを使用する

转自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

おすすめ

転載: blog.csdn.net/qq_32198277/article/details/87000469