6

golang小技巧

 3 years ago
source link: http://www.hi-roy.com/2020/12/16/golang%E5%B0%8F%E6%8A%80%E5%B7%A7/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

golang小技巧

12月 16, 2020 发布在 Golang

下划线的妙用

大多数情况下,下划线_都用于忽略函数返回值的情况。最近查看authboss这个开源库的代码时,发现这样一种用法:

1
2
3
4
5
6
7
8
9
10
struct User {
...
}

var (
assertUser = &User{}
assertStorer = &MemStorer{}
_ authboss.User = assertUser
_ authboss.AuthableUser = assertUser
)

其中authboss.User是一个接口:

1
2
3
4
type User interface {
GetPID() (pid string)
PutPID(pid string)
}

这里下划线的作用就是在编译时进行检查,确保User完全实现了authboss.User接口。

此外还可以在声明结构体时使用这个技巧:

1
2
3
4
5
6
7
8
type User struct {
name string
age int
_ struct{}
}

// a := User{"bob",20} // 这种会报错too few values in SomeSturct literal
b := User{"name":"bob","age":20} // 通过检查

比如当开发阶段,接口定义、结构体定义还会经常变动时可以使用这个技巧,在编译阶段就发现问题。

关于errors

使用github.com/pkg/errors替换原生的errors包,这个包有3个关键函数:

  1. Warp用于对底层错误进行包装,添加上下文以及调用栈信息。通常建议用这个包装其他人的三方库或者标准库错误。
  2. WithMessage这个函数只用于对包装过的错误添加信息,注意不要重复Warp
  3. WithStack这个函数只用于添加调用栈而不用附加额外信息的情况。
  4. Cause用于获取底层错误。

借用个别人的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
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服务时很重要的一点就是处理完当前链接后再退出程序,这里就可以使用信号机制:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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,或者叫迪米特法则。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK