超好用的golang工具分享

go-callvis-代码调用关系的可视化工具

go-callvis是一个代码调用关系的可视化工具,它可以帮助我们了解指定项目代码的结构,以达到更快的理解代码意图的目的。

工具使用简单,步骤如下:

// 1. 安装
git clone https://github.com/ofabry/go-callvis.git
cd go-callvis && make install

// 2. 以著名golang开源项目bigcache 的main函数为入口,分析代码调用关系(不打开浏览器 | 忽略标准库的方法)
go-callvis -skipbrowser -nostd ./server/

// 3. 访问http://localhost:7878查看调用关系矢量图

调用关系矢量图怎么看,一共分为三个部分:

Packages / Types(包)

RepresentsStyle
focused(需要关注的)blue color(蓝色)
stdlib(标准库)green color(绿色)
other(其他包)yellow color(黄色)

Functions / Methods(函数方法)

RepresentsStyle
exported(导出的包)bold border(粗边框)
unexported(未导出包)normal border(正常边框)
anonymous(匿名包)dotted border(虚线边框)

Calls(调用)

RepresentsStyle
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-自动生成单测用例框架

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 simplefactory

import "fmt"

//API is interface
type API interface {
	Say(name string) string
}

//NewAPI return Api instance by type
func NewAPI(t int) API {
	if t == 1 {
		return &hiAPI{}
	} else if t == 2 {
		return &helloAPI{}
	}
	return nil
}

//hiAPI is one of API implement
type hiAPI struct{}

//Say hi to name
func (*hiAPI) Say(name string) string {
	return fmt.Sprintf("Hi, %s", name)
}

//HelloAPI is another API implement
type helloAPI struct{}

//Say hello to name
func (*helloAPI) Say(name string) string {
	return fmt.Sprintf("Hello, %s", name)
}

自动生成的测试用例框架,如下:

PS D:\code\golang-design-pattern\00_simple_factory> gotests.exe  -all .\simple.go
Generated TestNewAPI
Generated Test_hiAPI_Say
Generated Test_helloAPI_Say
package simplefactory

import (
        "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-multierror-多错误管理

在关于使用 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 main

import (
    "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 Append

if result != nil {
	result.ErrorFormat = func([]error) string {
		return "errors!"
	}
}

goleak-内存泄漏检查

goroutine 泄漏会导致内存中存活的 goroutine 数量不断上升,直到把主机的CPU和内存全部吃爆,最终以服务宕机为止。所以,我们会想到有没有一种方法,可以在代码部署之前,来检查程序中是否存在goroutine 泄漏。

Uber 公司的 Go 团队在 GitHub 开源了他们的goroutine 泄漏检测器出来,一个与单元测试结合使用的工具。 goleak 可以监控当前测试代码中泄漏的 goroutine。下面有一个 goroutine 泄漏的例子:

//demo.go
func leak() error {
    go func() {
        time.Sleep(time.Minute)
    }()

    return nil
}

//demo_test.go
func TestLeakFunction(t *testing.T) {
    defer goleak.VerifyNone(t)

    if err := leak(); err != nil {
        t.Fatal("error not expected")
    }
}

用例直接报错了,从报错信息中我们可以看到泄露的goroutine 的堆栈信息,以及 goroutine 的状态。

pprof性能分析+火焰图

Pprof是一个用于采样数据可视化和分析的工具。主要分析服务运行过程产生的:阻塞同步的堆栈信息,所有的goroutine堆栈信息,活动对象的内存分配信息,互斥锁的竞争持有者的堆栈,默认进行30s的CPU采样信息,查看创建新OS线程的堆栈信息等等。

我们可以利用prof进行性能监控,且可以生成监控信息文件,方便后续分析性能瓶颈或者是内存泄漏情况。

package main

import (
    "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.go

2. ./prof_demo

3. 手动登陆浏览器,通过http://locahost:6060/debug/pprof进行查看相关的监控信息文件

4. go install github.com/google/pprof@latest

5. yum install graphviz

// 查看火焰图
6. pprof -http=:6061  http://192.168.159.140:6060/debug/pprof/profile

jsoniter-高性能json序列化工具

go语言多数用于云原生中的网咯服务,因此一个常见的场景就是数据的序列化和反序列化,一般都是利用json进行。这里推荐采用jsoniter替换掉go原生encoding/json,两者接口一致,但jsoniter的性能远远超过encoding/json,Benchmark详见如下:

ns/opallocation bytesallocation times
std decode35510 ns/op1960 B/op99 allocs/op
easyjson decode8499 ns/op160 B/op4 allocs/op
jsoniter decode5623 ns/op160 B/op3 allocs/op

import jsoniter "github.com/json-iterator/go"

var json = jsoniter.ConfigCompatibleWithStandardLibrary
json.Marshal(&data)

json.Unmarshal(input, &data)

Reference

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.。的博客-优快云博客_golang pprof allocs 解释

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

于顾而言

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值