go-callvis是一个代码调用关系的可视化工具,它可以帮助我们了解指定项目代码的结构,以达到更快的理解代码意图的目的。
工具使用简单,步骤如下:
// 1. 安装git clone https://github.com/ofabry/go-callvis.gitcd go-callvis && make install// 2. 以著名golang开源项目bigcache 的main函数为入口,分析代码调用关系(不打开浏览器 | 忽略标准库的方法)go-callvis -skipbrowser -nostd ./server/// 3. 访问http://localhost:7878查看调用关系矢量图
调用关系矢量图怎么看,一共分为三个部分:
Packages / Types(包)
Represents | Style |
focused(需要关注的) | blue color(蓝色) |
stdlib(标准库) | green color(绿色) |
other(其他包) | yellow color(黄色) |
Functions / Methods(函数方法)
Represents | Style |
exported(导出的包) | bold border(粗边框) |
unexported(未导出包) | normal border(正常边框) |
anonymous(匿名包) | dotted border(虚线边框) |
Calls(调用)
Represents | Style |
internal(内部) | black color(黑色) |
external(外部) | brown color(棕色) |
static(静态函数) | solid line(实线) |
dynamic(动态函数) | dashed line(虚线) |
regular(常规函数) | simple arrow(简单箭头) |
concurrent(协程) | arrow with circle(箭头带圆圈) |
deferred(defer) | arrow with diamond(箭头带菱形) |
gotests工具可以帮我们自动生成单测用例框架,这样以来,我们只需要关注需要测试的业务代码逻辑即可,省去了大量的拷贝复制的重复劳动。
gotest工具使用也是十分方便,可以直接安装(go get -u
github.com/cweill/gotests/...)后用命令行($ gotests [options] PATH ...)的方式,或者也可以作为IDE的插件直接使用,如Emacs,Vim,Atom Editor,Visual Studio Code, andIntelliJ Goland. 这里以VS Code为例:
// 一个简单工厂模式代码实现package simplefactoryimport "fmt"//API is interfacetype API interface { Say(name string) string}//NewAPI return Api instance by typefunc NewAPI(t int) API { if t == 1 { return &hiAPI{} } else if t == 2 { return &helloAPI{} } return nil}//hiAPI is one of API implementtype hiAPI struct{}//Say hi to namefunc (*hiAPI) Say(name string) string { return fmt.Sprintf("Hi, %s", name)}//HelloAPI is another API implementtype helloAPI struct{}//Say hello to namefunc (*helloAPI) Say(name string) string { return fmt.Sprintf("Hello, %s", name)}
自动生成的测试用例框架,如下:
PS D:\code\golang-design-pattern\00_simple_factory> gotests.exe -all .\simple.goGenerated TestNewAPIGenerated Test_hiAPI_SayGenerated Test_helloAPI_Saypackage simplefactoryimport ( "reflect" "testing")func TestNewAPI(t *testing.T) { type args struct { t int } tests := []struct { name string args args want API }{ // TODO: Add test cases. } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := NewAPI(tt.args.t); !reflect.DeepEqual(got, tt.want) { t.Errorf("NewAPI() = %v, want %v", got, tt.want) } }) }}func Test_hiAPI_Say(t *testing.T) { }) }}func Test_helloAPI_Say(t *testing.T) { type args struct { name string } tests := []struct { name string h *helloAPI args args want string }{ // TODO: Add test cases. } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { h := &helloAPI{} if got := h.Say(tt.args.name); got != tt.want { t.Errorf("helloAPI.Say() = %v, want %v", got, tt.want) } }) }}PS D:\code\golang-design-pattern\00_simple_factory>
在关于使用 Go 语言的时候,开发者面对最大的挑战的年度调查中,错误(error)管理总是能引起很多争论。在并发环境处理 error 的场景下,或者在同一个 goroutine 中合并多个错误的场景下,Go 提供了很不错的包可以让多个错误的处理变得简单:来看看如何合并由单个 goroutine 生成的多个 error。
go-multierror提供了常用的多错误管理四种方式:
Building a list of errors / Accessing the list of errors / Checking for an exact error value
构建错误返回列表 / 访问误返回列表 / 检查错误列表中是否包含某个错误
package mainimport ( "fmt" "errors" multierror"github.com/hashicorp/go-multierror")func step1() error { return errors.New("xhihu")}func step2() error { return errors.New("yhihu")}func main() { var result error if err := step1(); err != nil { result = multierror.Append(result, err) } if err := step2(); err != nil { result = multierror.Append(result, err) } fmt.Printf(result.Error()) if merr, ok := result.(*multierror.Error); ok { // Use merr.Errors // merr.Errors -> []error } if errors.Is(result, os.ErrNotExist) { // err contains os.ErrNotExist } return}
Customizing the formatting of the errors / 自定义多错误时显示的整体的打印信息
var result *multierror.Error// ... accumulate errors here, maybe using Appendif result != nil { result.ErrorFormat = func([]error) string { return "errors!" }}
goroutine 泄漏会导致内存中存活的 goroutine 数量不断上升,直到把主机的CPU和内存全部吃爆,最终以服务宕机为止。所以,我们会想到有没有一种方法,可以在代码部署之前,来检查程序中是否存在goroutine 泄漏。
Uber 公司的 Go 团队在 GitHub 开源了他们的goroutine 泄漏检测器出来,一个与单元测试结合使用的工具。 goleak 可以监控当前测试代码中泄漏的 goroutine。下面有一个 goroutine 泄漏的例子:
//demo.gofunc leak() error { go func() { time.Sleep(time.Minute) }() return nil}//demo_test.gofunc TestLeakFunction(t *testing.T) { defer goleak.VerifyNone(t) if err := leak(); err != nil { t.Fatal("error not expected") }}
用例直接报错了,从报错信息中我们可以看到泄露的goroutine 的堆栈信息,以及 goroutine 的状态。
Pprof是一个用于采样数据可视化和分析的工具。主要分析服务运行过程产生的:阻塞同步的堆栈信息,所有的goroutine堆栈信息,活动对象的内存分配信息,互斥锁的竞争持有者的堆栈,默认进行30s的CPU采样信息,查看创建新OS线程的堆栈信息等等。
我们可以利用prof进行性能监控,且可以生成监控信息文件,方便后续分析性能瓶颈或者是内存泄漏情况。
package mainimport ( "fmt" "time" "log" "net/http" _ "net/http/pprof" "os" "runtime")func alloc(outCh chan<- int) { buf := make([]byte, 1024) outCh <- 0}func Leak() { outCh := make(chan int) go func() { if false { <-outCh } select {} }() tick := time.Tick(time.Second / 100) i := 0 for range tick { i++ fmt.Println(i) //一直分配内存,不释放放 go alloc(outCh)}func main() { log.SetFlags(log.Lshortfile | log.LstdFlags) log.SetOutput(os.Stdout) runtime.GOMAXPROCS(1) runtime.SetMutexProfileFraction(1) runtime.SetBlockProfileRate(1) // 需要性能分析的业务逻辑 go Leak() go func() { // 通过http://locahost:6060/debug/pprof进行查看相关的监控信息文件 if err := http.ListenAndServe(":6060", nil); err != nil { log.Fatal(err) } os.Exit(0) }() select{}}1. go build prof_demo.go2. ./prof_demo3. 手动登陆浏览器,通过http://locahost:6060/debug/pprof进行查看相关的监控信息文件4. go install github.com/google/pprof@latest5. yum install graphviz// 查看火焰图6. pprof -http=:6061 http://192.168.159.140:6060/debug/pprof/profile
go语言多数用于云原生中的网咯服务,因此一个常见的场景就是数据的序列化和反序列化,一般都是利用json进行。这里推荐采用jsoniter替换掉go原生encoding/json,两者接口一致,但jsoniter的性能远远超过encoding/json,Benchmark详见如下:
ns/op | allocation bytes | allocation times | |
std decode | 35510 ns/op | 1960 B/op | 99 allocs/op |
easyjson decode | 8499 ns/op | 160 B/op | 4 allocs/op |
jsoniter decode | 5623 ns/op | 160 B/op | 3 allocs/op |
import jsoniter "github.com/json-iterator/go"var json = jsoniter.ConfigCompatibleWithStandardLibraryjson.Marshal(&data)json.Unmarshal(input, &data)
Go代码调用链路可视化工具—go-callvis - 知乎 (zhihu.com)
GoTests工具自动化test使用 - 掘金 (juejin.cn)
Go: Multiple Errors Management. Error management in Go is always prone… | by Vincent Blanchon | A Journey With Go | Medium
Fastest JSON parser ever (jsoniter.com)
Go:多错误管理 - Go语言中文网 - Golang中文社区 (studygolang.com)
GitHub - hashicorp/go-multierror: A Go (golang) package for representing a list of errors as a single error
Go: Goroutine 泄漏检查器 - Go语言中文网 - Golang中文社区 (studygolang.com)
golang性能优化之pprof及其火焰图 - 简书 (jianshu.com)
Golang-PProf之性能剖析_-Xx.。的博客-CSDN博客_golang pprof allocs 解释