惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

N
Netflix TechBlog - Medium
Blog — PlanetScale
Blog — PlanetScale
月光博客
月光博客
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
爱范儿
爱范儿
量子位
博客园 - 聂微东
Engineering at Meta
Engineering at Meta
WordPress大学
WordPress大学
GbyAI
GbyAI
MyScale Blog
MyScale Blog
IT之家
IT之家
P
Proofpoint News Feed
M
MIT News - Artificial intelligence
The Cloudflare Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Hugging Face - Blog
Hugging Face - Blog
The Register - Security
The Register - Security
Microsoft Security Blog
Microsoft Security Blog
博客园_首页
MongoDB | Blog
MongoDB | Blog
F
Fortinet All Blogs
博客园 - 三生石上(FineUI控件)
Y
Y Combinator Blog
雷峰网
雷峰网
V
Visual Studio Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
Last Week in AI
Last Week in AI
博客园 - 叶小钗
D
DataBreaches.Net
B
Blog
B
Blog RSS Feed
大猫的无限游戏
大猫的无限游戏
aimingoo的专栏
aimingoo的专栏
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
The GitHub Blog
The GitHub Blog
云风的 BLOG
云风的 BLOG
Recent Announcements
Recent Announcements
阮一峰的网络日志
阮一峰的网络日志
小众软件
小众软件
腾讯CDC
T
Threat Research - Cisco Blogs
SecWiki News
SecWiki News
Martin Fowler
Martin Fowler
D
Docker
Cisco Talos Blog
Cisco Talos Blog
T
Tenable Blog
Webroot Blog
Webroot Blog
宝玉的分享
宝玉的分享

hanjm's blog

深入理解Prometheus(GO SDK及Grafana基本面板) 深入理解Prometheus(GO SDK及Grafana基本面板) 深入理解ActiveMQ消息队列协议STMOP AMQP MQTT Macos Docker container连接宿主机172.17.0.1的办法 Nginx With gRPC编译安装 Go sql.Driver的mysql Driver 中的一个有意思的行为 学习Influxdb GRPC文档阅读心得 Go如何优雅地错误处理(Error Handling and Go 1) Go如何优雅地错误处理(Error Handling and Go 1) 深入理解NATS & NATS Streaming (踩坑记) 深入理解GO时间处理(time.Time) 深入理解GO时间处理(time.Time) Go如何精确计算小数-Decimal研究-Tidb MyDecimal问题 DockerContainer下gdb无法正常工作的解决办法 Go sync.Pool Slice Benchmark GO Logger 日志实践 Linux Cli下酷工具收集(持续) MacOS下酷工具收集(持续) Linux Cli下酷工具收集(持续) MacOS下酷工具收集(持续) 知名公司架构资料整理(持续) 知名公司架构资料整理(持续) Mysql 连接池问题 Go strings.TrimLeft() strings.TrimPrefix().md
Go最佳实践
本文作者: hanjm · 2017-06-24 · via hanjm's blog

发表于 |

来自NSQ

nsq的官方文档的Dsign中提到一个PPThttps://speakerdeck.com/snakes/nsq-nyc-golang-meetup, 里面有这样一段话

总结一下.

  1. 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)
  1. 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 main

import (
"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 <- i
ch <- -1
}

time.Sleep(time.Second * 5)
}

func worker(ch <-chan int, goId int) {

log.Printf("worker%d running", goId)
for data := range ch {
func() {
defer func() {
if err := recover(); err != nil {
log.Printf("worker%d recover error:%s", goId, err)
}
}()
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.

  1. watch your allocations (string() is costly, re-user buffers)
    go里面 []byte和string互转是会发生复制的, 开销明显, 如果代码里频繁互转, 考虑使用bytes.buffer 和 sync.Pool

  2. use anonymous structs for arbitrary JSON
    在写http api时, parse body这种事情, 如果只是纯粹取body里的json数据, 没必要单独定义结构体, 在函数里定义一个匿名结构体就好. var s struct { A int}

  3. no built-in per-request HTTP timeouts
    这是说要注意默认的httpClient没有超时

  4. synchronizing goroutine exit is hard - log each cleanup step in long-running goroutines
    同步化的goroutine一不小心就没有退出, 如果你写一个长期运行的服务, 用logger记录每一个goroutine的清理退出, 防止goroutine泄露

  5. 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

  1. 在go里, goroutines之间通信不要用共享内存的方式实现, 应该用channel来实现
  2. 并发不是并行
  3. channel是编排, mutexs是串行
  4. interface定义越多的方法, 抽象程度越低. Go提倡用接口组合的方式实现更大的接口
  5. 零值, 猜测这里说的是struct{}吧, struct{}是一个不占内存的空结构体, 在用map实现set, channel发送无额外意义的signal时能降低内存分配
  6. 提倡gofmt
  7. 一点点复制比一点点依赖好. 官方包里有时能见到一些复制的代码, 这是为了不互相依赖
  8. syscall每个平台实现不一样, 要加build tags
  9. cgo每个平台的lib不一样, 要加build tags
  10. Cgo不是go
  11. unsafe包不提供保障
  12. 简洁胜过高效
  13. error是值 可以用值的方式去处理错误: 传递, 比较
  14. 不用仅检查错误, 要优雅地处理
  15. 多花精力设计架构, 模块命名, 写详细的文档
  16. 写良好的文档给用户
  17. 对于普通错误, 应该用多值返回错误, 而不是手动panic

实践

  1. 写可重复使用的函数, 接收接口类型, 返回具体类型
  2. 写可扩展便于二次包装的函数, 接收接口类型, 返回接口类型. 如标准库的database/sql包

传值还是传指针

  1. 指针仅用于要修改值的场景和反射, 其他场景尽可能地用值传递, 能让变量尽可能分配在栈上, 减少GC压力, 提高性能. 还能减少nil 参考 https://segment.com/blog/allocation-efficiency-in-high-performance-go-services/

Golang Github

https://github.com/golang/go/wiki/CodeReviewComments