Go最佳实践
来源:互联网 发布:泰牛程序员php视频 编辑:程序博客网 时间:2024/05/19 03:20
来自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.Pooluse 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
- Go最佳实践
- 读生产环境下go语言最佳实践有感
- 产品环境中 Go 语言的最佳实践
- 产品环境中 Go 语言的最佳实践
- Go实践
- 最佳实践
- 最佳实践
- 最佳实践
- Go语言(golang)包设计哲学/原则与项目结构组织最佳实践
- go context专题(四)- context 最佳实践和相关争议
- Go语言最佳IDE
- Go实践-文件系统操作
- Go并发编程实践
- Go并发编程实践
- Go并发编程实践
- ANT十五大最佳实践
- C 编程最佳实践
- J2EE 最佳实践
- 知识图谱构建摸索
- Python gensim基础实战
- c库函数
- 六度分离
- 添加功能页面后再授权时不显示在列表中
- Go最佳实践
- JDBD工具类
- IT风投
- Struts2中validate数据校验的两种常用方法
- 配置Redis主从
- SQLHelper
- Ubuntu安装pycurl
- Linux的tomcat下部署solr
- MessagePack编解码功能测试代码补齐