关于Go你不得不知道的小技巧2

简介: 关于Go你不得不知道的小技巧2

不要依赖于计算顺序,特别是在 return 语句中。

  // BAD
  return res, json.Unmarshal(b, &res)
  // GOOD
  err := json.Unmarshal(b, &res)
  return res, err

防止结构体字段用纯值方式初始化,添加 _ struct {} 字段:

type Point struct {
  X, Y float64
  _    struct{} // to prevent unkeyed literals
}

对于 Point {X:1,Y:1} 都可以,但是对于 Point {1,1} 则会出现编译错误:

./file.go:1:11: too few values in Point literal

当在你所有的结构体中添加了 _ struct{} 后,使用 go vet 命令进行检查,(原来声明的方式)就会提示没有足够的参数。

为了防止结构比较,添加 func 类型的空字段

  type Point struct {
    _ [0]func() // unexported, zero-width non-comparable field
    X, Y float64
  }

http.HandlerFunc 比 http.Handler 更好

用 http.HandlerFunc 你仅需要一个 func,http.Handler 需要一个类型。


移动 defer 到顶部

这可以提高代码可读性并明确函数结束时调用了什么。


JavaScript 解析整数为浮点数并且你的 int64 可能溢出

用 json:"id,string" 代替

type Request struct {
  ID int64 `json:"id,string"`
}

并发

** 以线程安全的方式创建单例(只创建一次)的最好选择是 sync.Once


不要用 flags, mutexes, channels or atomics

** 永远不要使用 select{}, 省略通道, 等待信号


** 不要关闭一个发送(写入)管道,应该由创建者关闭


往一个关闭的 channel 写数据会引起 panic

** math/rand 中的 func NewSource(seed int64) Source 不是并发安全的,默认的 lockedSource 是并发安全的。


** 当你需要一个自定义类型的 atomic 值时,可以使用 atomic.Value


性能

  • ** 不要省略defer
  • 在大多数情况下 200ns 加速可以忽略不计
  • ** 总是关闭 http bodydefer r.Body.Close()
  • 除非你需要泄露 goroutine
  • ** 过滤但不分配新内存


  b := a[:0]
  for _, x := range a {
      if f(x) {
        b = append(b, x)
      }
  }

为了帮助编译器删除绑定检查,请参见此模式 _ = b [7]

** time.Time 有指针字段 time.Location 并且这对 go GC 不好


只有使用了大量的 time.Time 才(对性能)有意义,否则用 timestamp 代替

** regexp.MustCompile 比 regexp.Compile 更好


在大多数情况下,你的正则表达式是不可变的,所以你最好在 func init 中初始化它

** 请勿在你的热点代码中过度使用 fmt.Sprintf. 由于维护接口的缓冲池和动态调度,它是很昂贵的。


如果你正在使用 fmt.Sprintf("%s%s", var1, var2), 考虑使用简单的字符串连接。

如果你正在使用 fmt.Sprintf("%x", var), 考虑使用 hex.EncodeToString or strconv.FormatInt(var, 16)

** 如果你不需要用它,可以考虑丢弃它,例如io.Copy(ioutil.Discard, resp.Body)


HTTP 客户端的传输不会重用连接,直到body被读完和关闭。

  res, _ := client.Do(req)
  io.Copy(ioutil.Discard, res.Body)
  defer res.Body.Close()
  • ** 不要在循环中使用 defer,否则会导致内存泄露
  • 因为这些 defer 会不断地填满你的栈(内存)
  • ** 不要忘记停止 ticker, 除非你需要泄露 channel
  ticker := time.NewTicker(1 * time.Second)
  defer ticker.Stop()
  • ** 用自定义的 marshaler 去加速 marshaler 过程
  • 但是在使用它之前要进行定制!
  func (entry Entry) MarshalJSON() ([]byte, error) {
    buffer := bytes.NewBufferString("{")
    first := true
    for key, value := range entry {
        jsonValue, err := json.Marshal(value)
        if err != nil {
            return nil, err
        }
        if !first {
            buffer.WriteString(",")
        }
        first = false
        buffer.WriteString(key + ":" + string(jsonValue))
    }
    buffer.WriteString("}")
    return buffer.Bytes(), nil
  }
  • **
    sync.Map 不是万能的,没有很强的理由就不要使用它。
  • **
    sync.Pool 中分配内存存储非指针数据
  • **
    为了隐藏逃生分析的指针,你可以小心使用这个函数::


  // noescape hides a pointer from escape analysis.  noescape is
  // the identity function but escape analysis doesn't think the
  // output depends on the input. noescape is inlined and currently
  // compiles down to zero instructions.
  //go:nosplit
  func noescape(p unsafe.Pointer) unsafe.Pointer {
    x := uintptr(p)
    return unsafe.Pointer(x ^ 0)
  }

**


对于最快的原子交换,你可以使用这个 m := (*map[int]int)(atomic.LoadPointer(&ptr))


**


如果执行许多顺序读取或写入操作,请使用缓冲 I/O


减少系统调用次数

**


有 2 种方法清空一个 map:


重用 map 内存 (但是也要注意 m 的回收)

  for k := range m {
    delete(m, k)
  }
  • 分配新的
  m = make(map[int]int)

构建

** 用这个命令 go build -ldflags="-s -w" ... 去掉你的二进制文件


** 拆分构建不同版本的简单方法


用 // +build integration 并且运行他们 go test -v --tags integration .

** 最小的 Go Docker 镜像


https://twitter.com/bbrodriges/status/873414658178396160

CGO_ENABLED=0 go build -ldflags="-s -w" app.go && tar C app | docker import - myimage:latest

** run go format on CI and compare diff


这将确保一切都是生成的和承诺的

** 用最新的 Go 运行 Travis-CI,用 travis 1


** 检查代码格式是否有错误 diff -u <(echo -n) <(gofmt -d .)


测试

** 测试名称 package_test 比 package 要好

** go test -short 允许减少要运行的测试数


  func TestSomething(t *testing.T) {
    if testing.Short() {
      t.Skip("skipping test in short mode.")
    }
  }
  • ** 根据系统架构跳过测试
  if runtime.GOARM == "arm" {
    t.Skip("this doesn't work under ARM")
  }

** 用 testing.AllocsPerRun 跟踪你的内存分配


** 多次运行你的基准测试可以避免噪音。


go test -test.bench=. -count=20

工具

**


快速替换 gofmt -w -l -r "panic(err) -> log.Error(err)" .


**


go list 允许找到所有直接和传递的依赖关系


go list -f '{{ .Imports }}' package

go list -f '{{ .Deps }}' package

**


对于快速基准比较,我们有一个 benchstat 工具。


**


go-critic linter 从这个文件中强制执行几条建议


**


go mod why -m <module> 告诉我们为什么特定的模块在 go.mod 文件中。


**


GOGC=off go build ... 应该会加快构建速度 source


**


内存分析器每 512KB 记录一次分配。你能通过 GODEBUG 环境变量增加比例,来查看你的文件的更多详细信息。


**


go mod why -m <module> 告诉我们为什么特定的模块是在 go.mod 文件中。


其他

** dump goroutines

  go func() {
    sigs := make(chan os.Signal, 1)
    signal.Notify(sigs, syscall.SIGQUIT)
    buf := make([]byte, 1<<20)
    for {
      <-sigs
      stacklen := runtime.Stack(buf, true)
      log.Printf("=== received SIGQUIT ===\n*** goroutine dump...\n%s\n*** end\n"  , buf[:stacklen])
    }
  }()

** 在编译期检查接口的实现

  var _ io.Reader = (*MyFastReader)(nil)
  • ** len(nil) = 0
  • ** 匿名结构很酷
  var hits struct {
    sync.Mutex
    n int
  }
  hits.Lock()
  hits.n++
  hits.Unlock()

**


httputil.DumpRequest 是非常有用的东西,不要自己创建


**


获得调用堆栈,我们可以使用 runtime.Caller


**


要 marshal 任意的 JSON, 你可以 marshal 为 map[string]interface{}{}


**


配置你的 CDPATH 以便你能在任何目录执行 cd github.com/golang/go


添加这一行代码到 bashrc(或者其他类似的) export CDPATH=$CDPATH:$GOPATH/src

**


从一个 slice 生成简单的随机元素


[]string{"one", "two", "three"}[rand.Intn(3)]


相关文章
|
2月前
|
开发框架 安全 中间件
Go语言开发小技巧&易错点100例(十二)
Go语言开发小技巧&易错点100例(十二)
31 1
|
2月前
|
Go
Go语言开发小技巧&易错点100例(十一)
Go语言开发小技巧&易错点100例(十一)
16 0
|
2月前
|
存储 Java Go
Go语言开发小技巧&易错点100例(十)
Go语言开发小技巧&易错点100例(十)
19 0
|
2月前
|
Go 开发者
Go语言开发小技巧&易错点100例(九)
Go语言开发小技巧&易错点100例(九)
15 0
|
2月前
|
JSON Go 数据格式
Go语言开发小技巧&易错点100例(八)
Go语言开发小技巧&易错点100例(八)
17 0
|
2月前
|
消息中间件 IDE Go
Go语言开发小技巧&易错点100例(七)
Go语言开发小技巧&易错点100例(七)
18 0
|
2月前
|
运维 监控 Go
Go语言开发小技巧&易错点100例(六)
Go语言开发小技巧&易错点100例(六)
20 0
|
2月前
|
Java Go
Go语言开发小技巧&易错点100例(五)
Go语言开发小技巧&易错点100例(五)
20 0
|
2月前
|
Go
Go语言开发小技巧&易错点100例(四)
Go语言开发小技巧&易错点100例(四)
22 0
|
2月前
|
JSON Go 数据格式
Go语言开发小技巧&易错点100例(三)
Go语言开发小技巧&易错点100例(三)
17 0