Go Tips

这些 tips 是有本人日常开发所遇到过的 issue 后记录下来的解决方法,会不定期更新。

NOOP interface #

2023-10-15 08:40
默认的interface可以是NOOP,以兼容(避免)或者说最大程度较少代码的侵入性

以OpenTelemetr为例子,没有配置 tracer 提供者(provider),所以返回的是一个 Noop tracer,对主体代码的运行没有影响.

言简意赅的go scheduler解释 #

2021-01-02T06:29:29+08:00

concurrency好,scheduler就得好。
Rob Pike的'Concurrency Is Not Parallelism'

scheduler的成本主要是:
a) communication
b) migration

scheduler方式主要有2种:
work-sharing: 自己忙就往其他process上扔
work-stealing: 自己空闲就从其他process上偷几个来干

go用的是work-stealing,借鉴于CILK设计论文,tokudb在最早的时候也用了CILK。

粗讲很简单:
就是每个P(process structure)上有个 runnable goroutines list, goroutine结束一个就从这个list摘除一个,如果list空了,那就去“偷”活干,communication和migration都比较低。

来源于BohuTANG

go tooling in action #

2017-09-02 14:29:29
go 的工具链对于不管是在编码工作,编码规范,性能分析 tunning 方面基本说是必备的,这个视频 以及提供了 配套练习 简洁快速干货,值得一看。

internal package #

在使用goimport进行包自动引入时,如果不同项目含有一样的包后缀和方法,可能会出现引入错误,而放在 internal 文件夹下面的 package 只能被当前的项目所引用就能很好的解决这个瘙痒问题。如果把 internal 放在 project 下面的第一层,那么 internal 里面的包就只能被该 project 引用了。

version on compilation #

对于不定时发布新版本程序在线上的项目而言,当出现问题时往往需要查看当前发布的是哪一个版本的程序(或者哪一次 commit); 一般进行新版本程度分发部署和不同机子上都先在本地编译成 executal app 再同步到各个环境上,只要在编译的命令中指定变量的值为版本号,可以用 git commit 的 hash 值来充当;一般可以写个 bash 或者 makefile 这种 script 来进行自动编译。
adding-version.go

tag version on vendor package #

2022-08-20 09:05:48
像用go mod 时通过$targetVersionPackage.Version的方式可以给Version赋值打上版本信息,但对于vendor管理包的方式则是
使用$projectPackage/vendor/$targetVersionPackage.Version 的方式去指定Version的值

vendor package management #

不同项目中使用 github.com 的第三方包存在版本更新不同步的情况,把这些三方包放到 vendor 目录中,就可以让项目使用 vendor 下面的 package 而不会引用$GOPATH/src这种共用版本的 package, 项目单据维护自己 vendor 下的三方包即可。

exit goroutines gracefully #

WaitGroup 来等待和确保所有 goroutine 退出
已经关闭的 channel 是 readable 的,可以用来通知 goroutine 退出;

exit := make(chan struct{})
var wg sync.WaitGroup
const count = 9
wg.Add(count)
for i := 0; i < count; i++ {
    j := i
    go func(goID int) {
        for {
            select {
            case <-exit:
                wg.Done()
                println("exit ", goID)
                return
            }
        }
    }(j)
}
close(exit)
wg.Wait()

reference:Close Channels Gracefully in Golang

JSON2Go #

https://mholt.github.io/json-to-go/
利用示例转换成 go struct 一件很省心的事。

get proxy issue #

如果要让 go get走代理,可以在调用命令时设置 proxy
e.g.
https_proxy=http://127.0.0.1:7777 go get -u golang.org/x/tools/...
如果只有 sock5 代理,使用 cow 将 http 或者 https 转发代理到 sock5
e.g.
安装好 cow 后,设置~/.cow/rc 文件,启动 cow 即可

cd $GOPATH/bin && curl -L git.io/cow | bash

listen = http://127.0.0.1:7777
proxy = socks5://127.0.0.1:1080

go get vim-go binary tools

https_proxy=http://127.0.0.1:7777 go get -u -v golang.org/x/tools/...
https_proxy=http://127.0.0.1:7777 go get -u -v github.com/zmb3/gogetdoc/...
https_proxy=http://127.0.0.1:7777 go get -u -v  github.com/golang/lint/golint
https_proxy=http://127.0.0.1:7777 go get -u -v github.com/kisielk/errcheck
https_proxy=http://127.0.0.1:7777 go get -u -v github.com/josharian/impl

go range issue #

each time when you are using range, just think about it like this:

var value Foo
for var i := 0; i < len(list); i++ {
    value = list[i]
    list2[i] = &value
}

traps like bellow should be escaped:

	for _, v := range ss {
        go f(){
        //do something with v
        } ()
	}

or

	smap := make(map[int]*A, 3)
	for k, v := range ss {
		smap[k] = &v
	}

limit goroutine spawning #

2020-06-25 22:51:43
主要是 申请归还 的思路,根据block channel, 当想要启用一个goroutine来执行任务时,尝试写入channel,如果能写入说明目前运行的goroutine数量还未达到阀值,反之blocking的时候只有在 一个完成任务的goroutine从channel中取出占位资源后,这样就有一个空闲的goroutine可用了,部分关键代码如下:

const maxGoroutines = 10
guard := make(chan struct{}, maxGoroutines)
for {
    select {
    case <-exit:
        return
    case oneJob := <-jobQueuePending:
        //写入 channel
        guard <- struct{}{}
        go func() {
            //归还资源
            <-guard
        }()
    }
}

也或者同时启用 maxGoroutines 个goroutine,每个goroutine去同时抢一个channel上的任务

var wgWorkers sync.WaitGroup
for i := 0; i < maxGoroutines; i++ {
    wgWorkers.Add(1)
    go func(workerIndex int) {
        defer wgWorkers.Done()
        for {
            select {
            case <-exit:
                return
            case oneQa := <-queuePending:
                select {
                case queuePersist <- oneQa:
                case <-exit:
                    return
                }
            }
        }
    }(i)
}

wgWorkers.Wait()

skip checking some packages #

2021-01-26 10:17:32
比如不想检查项目中的 cmd 包

PACKAGES2CHECK ?= $(shell $(GO) list ./... | grep -v /cmd/)

vet:
    $(GO) vet $(PACKAGES2CHECK)

2022-12-18