Go提高编程质量和提高Go性能|青训营笔记

185 阅读4分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的的第3篇笔记

抖音--服务架构

性能体验方向

主要工作;

  • 服务性能优化
  • 降低资源占用成本

在工作中,或者优化服务方面还有哪些经验呢?

  • 如何编写更简洁更清晰的代码
  • 常用Go语言程序优化手段
  • 熟悉Go程序性能分析工具
  • 了解工程中性能优化的原则和流程

课程:

  1. 高质量编程
  • 高质量编程的见解
  • 编码规范
  • 性能优化建议
  1. 性能调优实战
  • 性能调优简介
  • 性能分析工具pprof实战
  • 性能调优案例

一、高质量编程

\

1.1 简介

推荐阅读 clear code

\

边界条件是否稳定

异常情况的处理,稳定性的保证

易读易维护

编程原则:

  1. 简单性----代码简洁
  2. 可读性----代码可读
  3. 生产力----提高整个团队的效率

1.2 编码规范

google具有编码规范,可以阅读

  • 代码格式
  • 注释
  • 命名规范
  • 控制流程
  • 错误和异常处理

1.2.1 代码格式

  • 使用gofmt自动格式代码

比如golang 自动内置格式化

  • 比如goimports自动对依赖包进行管理

\

1.2.2 编码规范----注释

\

注释应该做的:

  • 解释代码作用
  • 注释应该解释代码如何做
  • 注释应该解释代码实现的原因
  • 注释应该解释代码什么情况出错

\

  • 公共符号一定要注释

但是像接口这种,不需要提供核外的信息,没有用的注释不需要

总结:

  • 代码是最好的注释
  • 注释最好能够提供代码为表达的信息

1.2.3 编程规范---命名规范

  • variable

简洁胜于冗长 比如利用i 而不是index

缩小一定犬大小 比如serverHTTP而不是serverHttp

*

用1,因为调用的时候是http.serve

  • package

只用小写字母

简短但是包含一定的上下文

总结

  • 核心在于降低阅读理解代码的成本
  • 重点考虑上下文信息,设计简洁清晰的名称

1.2.4 编码规范--控制流程

重要

  1. 避免嵌套,保持正常流程规范

正常流程是return nil,而且不需要else 复杂冗余

if foo{
  return x
}else{
  return nil
}
if foo{
  return x
}
return nil
  1. 尽量保证正常代码路径为最小缩进
  • 也就是有限处理错误、特殊情况,尽早返回或者继续循环来减少嵌套

小结

  • 复杂和循坏的语句,容易出现错误

1.2.5 编码规范---错误和异常处理

简单错误

  • 指仅出现一次的错误,其他地方不需要捕获该错误
  • 优先使用errors.New来创建匿名变量来直接表示简单错误
  • 如果有格式化需求,使用fmt.Errorf
func defaultCheckRedict(via []*Request) error{
  if len(via)>=10{
    return errors.New("stopped after 10 redirects")
  }
  return nil
}

复杂错误---提供错误的包装(wrap)和解包装(unwrap)的能力

  • 错误的wrap实际上是提供了一个error嵌套另一个error的能力,从而生成一个error的跟踪链
  • 在fmt.Errorf中使用:%w关键字来将一个错误关联至错误链中
if err!=nil{
  return fmt.Errorf("reading srcfiles list: %w",err)
}

错误判定---错误和异常处理

  • 判定一个错误是否为特定错误,使用errors.Is
  • 不同于使用==,使用该方法可以判定错误链上的所有错误是否含有特定的错误
data,err=lockfile(targ)
if errors.Is(err,fs.ErrNotExist){
    return []byte{},nil
}
return data,err
  • 在错误链上获取特定种类的错误,使用errors.As
if _,err:=...;err!=nil{
  var pathError *fs.PathError
  if errors.As(err,&pathError){
    fmt.Println("failed at path",pathError.Path)
  }else{
    fmt.Println(err)
  }
}
  • panic 会导致业务的出错

不建议在业务中使用panic

但是如果程序在启动阶段发生不可逆转的错误,可以在init和main函数中使用panic

  • recover----对应panic

recover 只能被defer在函数中使用

嵌套无法生效

只在当前goroutine生效

defer语句是后进先出的

1.3 性能优化的建议---benchmark

go 中利用benchmark来查看性能

补图

slice

如果容量足够,放入;如果不够,会进行扩容,从而降低性能

map

如果有预分配,map的性能会更好

  • 不断的向map添加元素会触发map的扩容
  • 提前分配好空间可以减少内存拷贝和rehash的消耗
  • 建议根据实际需求提前预估好需要的空间

字符串处理

  • stringbuilder----------最短
  • +----------性能最差
  • bytes.Buffer的方式----------性能和stringBuilder差不多

空结构体----节省空间的最好用法

  • 空结构体struct{}实例不占据任何内存空间
  • 可作为各种场景下的占位符使用
    • 节省资源
    • 空结构体本身具有很强的语义,这里不需要任何值,仅作为占位符
  • 实现set,可以使用map来替代
  • 对于这个场景,主需要用到map的键,而不需要值
  • 即使将map设置为bool,也会多占据一个字节空间

性能优化

2.1 简介

性能调优的原则:

  • 依靠数据而不是猜测
  • 要定位最大瓶颈而不是细枝末节
  • 不要过早优化
  • 不要过度优化

2.2 性能分析工具pprof