超好用的golang工具分享

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

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

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

go 复制代码
// 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(包)

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

gotests工具可以帮我们自动生成单测用例框架,这样以来,我们只需要关注需要测试的业务代码逻辑即可,省去了大量的拷贝复制的重复劳动。

gotest工具使用也是十分方便,可以直接安装(go get -u github.com/cweill/gote......)后用命令行($ gotests options PATH ...)的方式,或者也可以作为IDE的插件直接使用,如Emacs,Vim,Atom Editor,Visual Studio Code, andIntelliJ Goland. 这里以VS Code为例:

go 复制代码
// 一个简单工厂模式代码实现

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)
}

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

go 复制代码
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

构建错误返回列表 / 访问误返回列表 / 检查错误列表中是否包含某个错误

go 复制代码
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 / 自定义多错误时显示的整体的打印信息

go 复制代码
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 泄漏的例子:

go 复制代码
//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进行性能监控,且可以生成监控信息文件,方便后续分析性能瓶颈或者是内存泄漏情况。

go 复制代码
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/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
kotlin 复制代码
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.。的博客-CSDN博客_golang pprof allocs 解释

相关推荐
GetcharZp1 天前
GitHub 49K+ Star!C++ 开发者必知的 JSON 神级库:从零到精通全指北
后端
xujinwei_gingko1 天前
SpringBoot整合WebSocket
spring boot·后端·websocket
智码看视界1 天前
现代Web开发基础:全栈工程师的起航点
前端·后端·c5全栈
程序员cxuan1 天前
Claude Fable 5 来了
人工智能·后端·程序员
JS菌1 天前
手写一个 AI Agent 全栈项目:从沙箱执行到子智能体的完整实现
前端·人工智能·后端
wang09071 天前
自己动手写一个spring之IOC_2
java·后端·spring
ltl1 天前
推理退化:为什么大模型会输出乱码、死循环和无意义文本
后端
ltl1 天前
架构视图与文档:C4 模型从入门到实战
后端
IT_陈寒1 天前
Redis持久化这个坑,我爬了一整天才出来
前端·人工智能·后端
无风听海1 天前
多租户系统中的 OIDC:Discovery 端点与联合登录的深度实践
后端·python·flask