记录一些 Go 应用开发时可能遇到的坑以备未来参考。个人见解或许有误。
- 注意限制传入 make 的长度范围,尤其是基于外部输入的时候,避免内存爆炸。错误写法范例: https://github.com/mumble-voip/grumble/blob/181a4f321998046264246cfeb348803636c1c386/cmd/grumble/client.go#L246-L252
// Read the message length (32-bit big-endian unsigned integer)
err = binary.Read(client.reader, binary.BigEndian, &length)
if err != nil {
return
}
buf := make([]byte, length)
- 尽量减少 make 的使用,hot path 中可以考虑使用 sync.Pool 复用(限定最大长度)。正确写法范例: https://github.com/xtaci/kcp-go/blob/582575ef06d2207d68aeb6c8ce71605a42ace110/sess.go#L47-L58 https://github.com/xtaci/kcp-go/blob/582575ef06d2207d68aeb6c8ce71605a42ace110/sess.go#L566 https://github.com/xtaci/kcp-go/blob/582575ef06d2207d68aeb6c8ce71605a42ace110/sess.go#L338
var (
// a system-wide packet buffer shared among sending, receiving and FEC
// to mitigate high-frequency memory allocation for packets, bytes from xmitBuf
// is aligned to 64bit
xmitBuf sync.Pool
)
func init() {
xmitBuf.New = func() interface{} {
return make([]byte, mtuLimit)
}
}
bts := xmitBuf.Get().([]byte)[:len(buf)]
xmitBuf.Put(s.txqueue[k].Buffers[0])
- 不要在包之间转移大量(变长)内存的所有权
- 不要一次开好多 goroutine。有条件制作几个队列,在一个固定大小的 goroutine 池中安排作业
- 用内置 http 库时 body.close 的 defer 不要写得太早
- 永远使用正常的错误包装库,如 pkg/errors
- 内置库很垃圾时可以看一下 golang.org/x 和 github.com/pkg 找轮子 (比如 golang.org/x/net/ipv4.PacketConn.SendBatch)
- channel 的 close 和 gc 之间没有屁点关系,两个之间没有直接决定关联。没有用 for range channel 就不要 close。更不要试图检查 channel 是不是关掉了,用一些 recover 处理异常以替代检查。错误写法范例: https://github.com/Mrs4s/MiraiGo/blob/7e587643012f6e8f0f1916f826de2e3057ac8ad6/client/group_msg.go#L71-L78
ch := make(chan int32)
c.onGroupMessageReceipt(eid, func(c *QQClient, e *groupMessageReceiptEvent) {
if e.Rand == mr && !utils.IsChanClosed(ch) {
ch <- e.Seq
}
})
defer c.onGroupMessageReceipt(eid)
defer close(ch)
- 留意一些莫名其妙的 struct 复制,尤其在给结构体起别名时
- 在 sync.Map 和加锁 map 之间权衡。如非不同组 key 操作一般取加锁 map。需要不停遍历所有元素时用加锁 map。
- 可以 atomic 就不用锁了(计数器)
- 文件少觉得混沌时可以试着多拆分几个文件
- 没事盯着 pprof 多看看
- 锁里边干事的时候千万小心不要又上锁(尤其是异常跳出的时候)
- 除非有十足把握一段代码不会 panic,否则不要不用 defer 的把 mutex 包在这段代码两边。可以使用下述奇技淫巧:
func () {
mutex.Lock()
defer mutex.Unlock()
// do shit
}()