01、高质量编程
1.1、什么是高质量编程
简单性:
- 消除多余的复杂性吗,以简单清晰的逻辑编写代码
- 不理解的代码无法修复改进
可读性:
- 代码是给人看的,不是给机器看的
- 编写可维护代码的第一步是确保代码可读
生产力:
- 团队整体工作效率非常重要
1.2、编码规范
- 代码格式
- 注释
- 注释应该解释代码的作用
- 注释应该解释代码如何做的
- 注释应该解释代码实现的原因
- 注释应该解释代码什么情况会出错
- 命名规范
variable- 简洁胜于冗长
- 缩略词全大写,但是当其位于变量开头且不需要导出的时候,使用小写
- 变量距离其被使用的地方越远,则需要携带越多的上下文信息
function:- 函数名不携带包名的上下文信息,且尽量简短。
- 当名为
foo的包的某个函数返回类型是Foo的时候,可以省略类型信息而不导致歧义。 - 当名为
foo的包的某个函数返回类型是T,T不是Foo,可以在函数名中加入类型信息。
package:- 只用小写字母组成,简短且包含一定上下文信息
- 不要与标准库同名
- 控制流程
- 错误和异常处理
- 简单错误:仅出现一次的错误,其他地方不需要捕获该错误
- 优先使用
errors.New来创建匿名变量来直接表示错误 - 如果由格式化需求,使用
fmt.Errorf
- 优先使用
- 错误的
Wrap和Unwrap- 错误的
Wrap实际上是提供了一个error嵌套另一个error的能力,从而生成一个error的跟踪链 - 在
fmt.Errorf中的使用:%w关键字来将一个错误关联至错误链中。
- 错误的
- 错误判定:
- 判断一个错误是否是特定错误,使用
errors.Is不同于==, 使用该方法可以判定错误链上的所有错误是否含有特定的错误。 - 在错误链上获取特定种类的错误,使用
error.As
- 判断一个错误是否是特定错误,使用
panic:不建议在业务代码中使用panic- 调用函数不包含
recover会造成崩溃,若问题可以被屏蔽或解决,建议使用error代替panic - 当程序启动阶段发生不可逆转的错误时,可以在
init或main函数中使用panic
- 调用函数不包含
recover:recover只能在被defer的函数中使用- 嵌套无法生效
- 只在当前
goroutine中生效 defer语句是后进先出的- 一般
recover后在log中记录当前的调用栈
- 简单错误:仅出现一次的错误,其他地方不需要捕获该错误
1.3、性能优化建议
-
slice预分配内存, 尽可能在使用make初始化切片时提供容量信息。 -
大内存未释放:在已有切片的基础上创建切片,不会创建新的底层数组,如果原来的切片比较大,代码在原来的基础上新建小切片,这时候底层数组在内存中有引用,得不到释放。可以用
copy代替re-slice -
map预分配内存。 -
字符串拼接使用
strings.Builder, 如果已知大小,可以使用Grow预分配内存。 -
空结构体节省内存
-
空结构体示例不占据任何内存空间
-
可以作为各种场景下的占位符使用
-
节省资源
-
空结构体本身具备很强的语义,即这里不需要任何值,仅作为占位符。
-
func EmptyStructMap(n int) { m := make(map[int]struct{}) for i := 0; i < n; i ++ { m[i] = struct{}{} } } -
空结构体
map可以实现set
-
-
-
如何使用
atomic包:-
type atomicCounter struct{ i int32 } func AtomicAddOne(c *atomicCounter) { atomic.AddInt32(&c, i, 1) } -
上面代码比加锁的效果好,因为
atomic是通过硬件实现的,锁是操作系统来实现的。
-
02、性能调优实战
2.1、性能优化分析工具 pprof
- 希望知道应用在什么地方耗费了多少
CPU、Memory - pprof是用于可视化和分析性能分析数据的工具
简单使用,首先从github.com/wolfogre/go… 下载下来调试代码,然后运行。
点击http://localhost:6060/debug/pprof/ 查看有哪些属性可以看。
2.1.2、查看CPU
在cmd中切换到项目所在位置,然后输入下面命令:
go tool pprof "http://localhost:6060/debug/pprof/profile?seconds=10"
等10秒钟,会进入pprof环境,然后输入top查看耗时的代码:
这里解释一下各个字段的含义:
| 字段名 | 含义 |
|---|---|
| flat | 代码自己的运行时间(不算调用别的程序) |
| flat% | 代码运行时间占cpu总时间的比例 |
| sum% | 统计前面几行的cpu使用率之和 |
| cum | 代码运行时间,包括调用别的程序的时间 |
| cum% | 代码运行时间占总时间的比例 |
考虑一下什么时候 ?
- :代码没有调用别的程序
- 代码一直在调用别的程序
如果要定位到哪一行代码出错,可以使用list xx
如果不想使用命令行看,可以使用web命令:
go tool pprof -http=:8080 "http://localhost:6060/debug/pprof/profile?seconds=10"
等运行完之后,会自动打开浏览器,小伙伴们需要先去下载一下Graphviz ,安装之后将其bin目录的全路径配置到环境变量中即可。
2.2.2、查看堆内存
在cmd中切换到项目所在位置,然后输入下面命令:
go tool pprof -http=:8080 "http://localhost:6060/debug/pprof/heap"
点击左上角的VIEW可以切换到其他视图,如top:
source视图,类似于上面的list命令:
左上角的sample可以切换采样
alloc_object:程序累计申请的对象数alloc_space:程序累计申请的内存大小inuse_objects:程序当前持有的对象数inuse_space:程序当前占用的内存大小
例如,切换到alloc_space状态下,可以发现还有一个地方申请大内存,因为没有用到,被gc了,所以不显示在inuse采样中
定位到这一行代码:
注释掉即可
2.2.3、Goroutine
在cmd中切换到项目所在位置,然后输入下面命令:
go tool pprof -http=:8080 "http://localhost:6060/debug/pprof/goroutine"
切换到Flame Graph视图下,从上到下表示调用顺序, 长度表示了这个代码占用cpu的时间:
右击某一个即可进入源码查看哪部分有问题,注释掉即可。
2.2.4、mutex 锁
在cmd中切换到项目所在位置,然后输入下面命令:
go tool pprof -http=:8080 "http://localhost:6060/debug/pprof/mutex"
2.2.5、block
在cmd中切换到项目所在位置,然后输入下面命令:
go tool pprof -http=:8080 "http://localhost:6060/debug/pprof/block"
......
不可视化的时候:
go tool pprof "http://localhost:6060/debug/pprof/block"
可以看到有一些程序被过滤掉了:
在http://localhost:6060/debug/pprof/ 下面的block条目下,可以查看隐藏的内容。