這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
來自NSQ
nsq的官方文檔的Dsign中提到一個PPThttps://speakerdeck.com/snakes/nsq-nyc-golang-meetup, 裡面有這樣一段話
總結一下.
- don’t be afraid of sync package
sync包裡有
- sync.Mutex(互斥鎖,一讀一寫)
- sync.RWMutex(讀寫鎖,可以多讀一寫)
- sync.Pool(對象池, 合理利用可以減少記憶體配置, 降低GC壓力, 稍後寫一篇部落格說說)
- sync.Once(並發控制. 適用於開幾個goroutines去執行一個只執行一次的任務, 比如單例模式)
- sync.Cond(並發控制, cond.Wait()阻塞至其他goroutie運行到cond.Signal())
- sync.WaitGroup(並發控制. 通常用法 wg.Add增加任務數量 goroutie完成任務後執行wg.Done,任務數量減1 wg.Wait等待wg任務數量為0)
- goroutines are cheap not free
這句話在其他地方也看過, go func()簡單好用, 建立開銷也很小, 但也是有開銷的. 很多情況下開固定數量worker, 用channel傳遞資料, 效果會更好.
go-apns2中的example是個非常好的例子.https://github.com/sideshow/apns2/blob/master/_example/channel/main.go
注意一個問題, go裡面一個goroutine panic了, 會導致進程退出, 所以go func()時第一行帶上
go func(){defer func(){if err:=recover(); err!=nil{}}()}()
是安全的做法, worker channel法時類似
package mainimport ("fmt""log""time")func main() {ch := make(chan int, 10)for i := 0; i < 2; i++ {go worker(ch, i)}for i := 0; i < 3; i++ {ch <- ich <- -1}time.Sleep(time.Second * 5)}func worker(ch <-chan int, goId int) {defer func(ch <-chan int) {if err := recover(); err != nil {log.Printf("worker%d recover error:%s", goId, err)go worker(ch, goId)}}(ch)log.Printf("worker%d running", goId)for data := range ch {log.Printf("worker%d received data:%d", goId, data)if data == -1 {panic(fmt.Errorf("worker%d panic", goId))}}}
fasthttp之所以快, 其中一個原因就是net/http是來一個串連就建立一個goroutie, 而fasthttp用了池複用了goroutines.
watch your allocations (string() is costly, re-user buffers)
go裡面 []byte和string互轉是會發生複製的, 開銷明顯, 如果代碼裡頻繁互轉, 考慮使用bytes.buffer 和 sync.Pool
use anonymous structs for arbitrary JSON
在寫http api時, parse body這種事情, 如果只是純粹取body裡的json資料, 沒必要單獨定義結構體, 在函數裡定義一個匿名結構體就好. var s struct { A int}
no built-in per-request HTTP timeouts
這是說要注意預設的httpClient沒有逾時
synchronizing goroutine exit is hard - log each cleanup step in long-running goroutines
同步化的goroutine一不小心就沒有退出, 如果你寫一個長期啟動並執行服務, 用logger記錄每一個goroutine的清理退出, 防止goroutine泄露
select skips nil channels
select語句是會跳過nil的channels的. 因為在Go裡往已經close掉的channel裡發送資料是會panic的, 可以利用select語句.
附: channel操作導致panic的情況有: 關閉一個nil的channel, 關閉一個已經關閉的channel( j,ok:= <- ch, ok為false時代表ch已經關閉了), 往一個已經關閉的channel裡發送資料(從已經關閉的channel裡讀資料是OK的, 如果這個channel是帶緩衝的, 那麼可以讀到所有資料)
來自GO箴言
Python有import this的zen of Python, 想不到Go也有箴言
https://speakerdeck.com/ajstarks/go-proverbs
- 在go裡, goroutines之間通訊不要用共用記憶體的方式實現, 應該用channel來實現
- 並發不是並行
- channel是編排, mutexs是串列
- interface定義越多的方法, 抽象程度越低. Go提倡用介面組合的方式實現更大的介面
- 零值, 猜測這裡說的是struct{}吧, struct{}是一個不佔記憶體的空結構體, 在用map實現set, channel發送無額外意義的signal時能降低記憶體配置
- 提倡gofmt
- 一點點複製比一點點依賴好. 官方包裡有時能見到一些複製的代碼, 這是為了不互相依賴
- syscall每個平台實現不一樣, 要加build tags
- cgo每個平台的lib不一樣, 要加build tags
- Cgo不是go
- unsafe包不提供保障
- 簡潔勝過高效
- error是值 可以用值的方式去處理錯誤: 傳遞, 比較
- 不用僅檢查錯誤, 要優雅地處理
- 多花精力設計架構, 模組命名, 寫詳細的文檔
- 寫良好的文檔給使用者
- 對於普通錯誤, 應該用多值返回錯誤, 而不是手動panic
未知來源
- 寫可重複使用的函數, 接收介面類型, 返回具體類型
Golang Github
https://github.com/golang/go/wiki/CodeReviewComments