Golang小技巧
下划线的妙用
大多数情况下,下划线_
都用于忽略函数返回值的情况。最近查看authboss
这个开源库的代码时,发现这样一种用法:
struct User {
...
}
var (
assertUser = &User{}
assertStorer = &MemStorer{}
_ authboss.User = assertUser
_ authboss.AuthableUser = assertUser
)
其中authboss.User
是一个接口:
type User interface {
GetPID() (pid string)
PutPID(pid string)
}
这里下划线的作用就是在编译时进行检查,确保User
完全实现了authboss.User
接口。
此外还可以在声明结构体时使用这个技巧:
type User struct {
name string
age int
_ struct{}
}
// a := User{"bob",20} // 这种会报错too few values in SomeSturct literal
b := User{"name":"bob","age":20} // 通过检查
比如当开发阶段,接口定义、结构体定义还会经常变动时可以使用这个技巧,在编译阶段就发现问题。
另外,如果在代码里import了一个包而没使用时,编译会报错。但在某些情况下,比如仅仅需要引用某些包调用其init
方法时,可以在包名前添加下划线,比如:
import (
"context"
_ "github.com/xxx/xxxx" // 这里仅需要执行其init函数而非需要使用其中的方法、结构等。
)
关于errors
使用github.com/pkg/errors
替换原生的errors
包,这个包有3个关键函数:
Warp
用于对底层错误进行包装,添加上下文以及调用栈信息。通常建议用这个包装其他人的三方库或者标准库错误。WithMessage
这个函数只用于对包装过的错误添加信息,注意不要重复Warp
。WithStack
这个函数只用于添加调用栈而不用附加额外信息的情况。Cause
用于获取底层错误。
借用个别人的例子:
import (
"database/sql"
"fmt"
"github.com/pkg/errors"
)
func foo() error {
return errors.Wrap(sql.ErrNoRows, "foo failed")
}
func bar() error {
return errors.WithMessage(foo(), "bar failed")
}
func main() {
err := bar()
if errors.Cause(err) == sql.ErrNoRows {
fmt.Printf("data not found, %v\n", err)
fmt.Printf("%+v\n", err)
return
}
if err != nil {
// unknown error
}
}
/*Output:
data not found, bar failed: foo failed: sql: no rows in result set
sql: no rows in result set
foo failed
main.foo
/usr/three/main.go:11
main.bar
/usr/three/main.go:15
main.main
/usr/three/main.go:19
runtime.main
...
*/
优雅结束任务
对外提供API服务时很重要的一点就是处理完当前链接后再退出程序,这里就可以使用信号机制:
var gracefulStop = make(chan os.Signal)
signal.Notify(gracefulStop, syscall.SIGTERM) // kill
signal.Notify(gracefulStop, syscall.SIGINT) // ctrl+c
// 如果是命令行
go func() {
sig := <-gracefulStop
// 一些清理工作
os.Exit(0)
}()
// 如果是http server
go func() {
sig := <-gracefulStop
server.Shutdown(ctx)
// 一些清理工作
os.Exit(0)
}()
那么问题就是,怎么知道清理完成可以退出了呢?最简单的直接sleep
几秒钟,但这个时间设定多久合适呢?
这里其实可以结合channel
来实现,比如这里的例子,这里就不贴代码了。
关于接口
关于接口,如果只需要有一种实现的话就别用接口了。另外定义接口时尽量分割成小的部分,保持最小知识原则LOD
,或者叫迪米特法则。