盤點Go中的開發神器

捉蟲大師發表於2022-03-01

本文已收錄 https://github.com/lkxiaolou/lkxiaolou 歡迎star。

在Java中,我們用Junit做單元測試,用JMH做效能基準測試(benchmark),用async-profiler剖析cpu效能,用jstack、jmap、arthas等來排查問題。
作為一名比較新的程式語言,golang的這些工具是否更加好用呢?

單元測試

Java的單元測試需要使用第三方庫,一般是Junit,配置起來比較複雜。在使用了golang之後發現golang自帶的單元測試真的非常簡單。
如果我們有一個cal.go檔案,那麼其對應的單元測試檔案為cal_test.go,其中的方法命名必須為TestXxx,這種按照命名進行單元測試的方式簡單有效,也正是通常所說的“約定大於配置”。
來看一個簡單的例子:

package unit

func add(a int, b int) int {
   return a + b
}

func sub(a int, b int) int {
   return a - b
}
package unit

import (
    "github.com/stretchr/testify/assert"
    "testing"
)

func TestAdd(t *testing.T) {
    assert.Equal(t, 10, add(5, 5))
}

func TestSub(t *testing.T) {
    assert.Equal(t, 0, sub(5, 5))
}

執行單元測試只需要執行(更多用法參考go help test)

go test --cover cal_test.go cal.go -v

image

benchmark

和單元測試類似,golang的benchmark也是開箱即用。在cal_test.go基礎上增加一個BenchmarkAdd方法

package unit

import (
   "github.com/stretchr/testify/assert"
   "testing"
)

func TestAdd(t *testing.T) {
   assert.Equal(t, 10, add(5, 5))
}

func TestSub(t *testing.T) {
   assert.Equal(t, 0, sub(5, 5))
}

func BenchmarkAdd(b *testing.B) {
   for i:= 0; i < b.N; i++ {
      add(5, 5)
   }
}

執行即可(更多用法參考go help test)

go test -bench=. -cpu=4 -count=3

image

pprof

pprof是golang自帶的可以用來做cpu、記憶體、鎖分析的工具,非常類似java的async-profiler。
pprof的使用非常簡單,只需要在程式碼中引入net/http/pprof包,然後監聽一個埠即可。
一個簡單的例子如下:

package main

import (
    "fmt"
    "log"
    "net/http"
    "time"
    _ "net/http/pprof"
)

func main() {
    go func() {
        //example: visit http://127.0.0.1:6060/debug/pprof in browser.
        err := http.ListenAndServe("0.0.0.0:6060", nil)
        if err != nil {
            fmt.Println("failed to start pprof goroutine:", err)
        }
    }()

    http.HandleFunc("/", handler)
    log.Fatal(http.ListenAndServe("localhost:8000", nil))
}

func handler(w http.ResponseWriter, r *http.Request) {
    time.Sleep(1 * time.Second)
    eat()
    time := time.Now().Unix() * 2 + 1000000
    fmt.Fprintf(w, "URL.Path = %q; time = %d\n", r.URL.Path, time)
}

func eat() {
    loop := 10000000000
    for i := 0; i < loop; i++ {
        // do nothing
    }
}

在命令列中輸入

go tool pprof http://127.0.0.1:6060/debug/pprof/profile

同時不停的請求,讓pprof能採集到資料,這裡我的請求是

curl http://127.0.0.1:8000/hello

等待30秒後,採集結束會顯示採集檔案的地址

Saved profile in /Users/roshi/pprof/pprof.samples.cpu.003.pb.gz

此時可以使用top等命令直接檢視cpu消耗過高的函式,更多命令可以使用help檢視。
image

或者把檔案下載下來用視覺化的介面來分析,可以使用

go tool pprof -http=":8080" /User/roshi/pprof/pprof.samples.cpu.003.pb.gz

來開啟一個視覺化的頁面,檢視,如果報錯需要安裝graphviz,安裝文件在這裡可以查詢:https://graphviz.gitlab.io/download/

訪問 http://localhost:8080/ui/ 可以看到下圖,其中面積最大的塊表示消耗cpu最多
image

這裡有一篇文章對pprof介紹的很仔細,可以參考:https://blog.wolfogre.com/posts/go-ppof-practice/

dlv

pprof很好用,但有一個缺點是必須事先在程式碼中開啟,如果線上出問題且沒有開啟pprof,可能就需要類似jstack、jmap、arthas等這類工具來排查。這裡推薦一個最近使用過非常好用的golang問題排查利器——dlv,專案地址見

https://github.com/go-delve/delve

它很有用的一個功能是attach,可以attach到正在執行的golang程式,檢視goroutine。這點可以很好的排查線上問題。
各個平臺的安裝在github上寫的很清楚,需要說明的是安裝dlv的golang版本和要排查程式的golang版本需要保持一致。
先寫一個測試程式,起兩個goroutine,一個執行,一個阻塞

package main

import (
   "fmt"
   "sync"
)

func main()  {
   go count()
   go wait()
   wait()
}

func count()  {
   count := 0
   for {
      count = count + 1
      if count % 1000000000 == 0 {
         fmt.Println("I'm a running routine")
      }
   }
}

func wait()  {
   wg := sync.WaitGroup{}
   wg.Add(1)
   wg.Wait()
}

執行起來,然後使用dlv進行attach,如下圖(具體命令可以attach後使用help檢視)

image

這樣很方便地看到了各個goroutine在幹啥


寫在最後

作為一門比較新的程式語言,golang對現有語言取其精華,自帶必要的工具,進一步降低門檻,對新手學習來說非常友好。


搜尋關注微信公眾號"捉蟲大師",後端技術分享,架構設計、效能優化、原始碼閱讀、問題排查、踩坑實踐。

image

相關文章