超好用的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 解释

相关推荐
吃面不喝汤661 小时前
Flask + Swagger 完整指南:从安装到配置和注释
后端·python·flask
讓丄帝愛伱2 小时前
spring boot启动报错:so that it conforms to the canonical names requirements
java·spring boot·后端
weixin_586062022 小时前
Spring Boot 入门指南
java·spring boot·后端
凡人的AI工具箱8 小时前
AI教你学Python 第11天 : 局部变量与全局变量
开发语言·人工智能·后端·python
是店小二呀9 小时前
【C++】C++ STL探索:Priority Queue与仿函数的深入解析
开发语言·c++·后端
canonical_entropy9 小时前
金蝶云苍穹的Extension与Nop平台的Delta的区别
后端·低代码·架构
我叫啥都行9 小时前
计算机基础知识复习9.7
运维·服务器·网络·笔记·后端
无名指的等待71210 小时前
SpringBoot中使用ElasticSearch
java·spring boot·后端
.生产的驴11 小时前
SpringBoot 消息队列RabbitMQ 消费者确认机制 失败重试机制
java·spring boot·分布式·后端·rabbitmq·java-rabbitmq
AskHarries11 小时前
Spring Boot利用dag加速Spring beans初始化
java·spring boot·后端