高质量编程与性能调优实战笔记分享 | 青训营笔记

93 阅读6分钟

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

3.1 高质量编程

3.1.1 高质量编程简介

高质量指:

各种边界条件是否考虑完备

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

易读易维护

3.1.2 编码规范

1、代码格式:

①使用gofmt自动格式化代码

②使用goimports管理依赖包

2、注释

①解释代码作用

②解释代码如何做的

③解释代码实现的原因

④解释代码什么情况会出错

3、命名规范

①变量

缩略词全大写,但当其位于变量开头且不需要导出时,使用全小写

例如:使用ServeHTTP而不是ServeHttp

使用XMLHTTPRequest或者xmlHTTPRequest

变量距离其被使用的地方越远,则需要携带越多的上下文信息,如在for循环遍历中使用 i 即可,因为它仅在当前for中有效

②函数

函数名不携带包名的上下文信息,因为包名和函数名总是成对出现

函数名尽量简短

image-20220523140144994

③包

只由小写字母组成。不包含大写字母和下划线等字符

简短并包含一定的上下文信息。例如schema、task等

不要与标准库同名,如不要使用sync或者strings

4、控制流程

①避免嵌套

image-20220523022640989

②尽量保持正常代码路径为最小缩进

image-20220523022742760image-20220523022801160

5、错误和异常处理

①简单错误

image-20220523023151341

②复杂错误

image-20220523023136900

③错误判定

image-20220523143335202

image-20220523143347422

④panic(不建议在业务中使用,用error即可)和recover

3.1.3 性能优化建议

3.1.3.1 使用标准库的BenchMark基准测试测试性能

使用命令go test -bench=. -benchmem可得到以下的结果

image-20220523154134070

3.1.3.2 slice

①尽可能在使用make()初始化切片时就设置好容量大小

从切片的底层原理去分析:这样可以减少切片自己扩充容量的次数,提升性能

image-20220523161029947

②在已有切片上创建切片,会复用原来的底层数组。在已有的大切片上创建小切片,会浪费大底层数组,因此建议先make一个小切片,再将大切片中需要的数据copy到小切片中

image-20220523162640600

3.1.3.3 map

预分配内存,同上面slice一样

3.1.3.4 string

字符串拼接的性能。常见的字符串拼接有三种方法:性能最快的是strings.Builder

原因是string在go中是不可变类型,占用内存大小是固定的,使用+来拼接的话每次都会重新分配内存;而strings.Builder底层是[]byte数组,有内存扩充策略

image-20220523163743131

image-20220523163756554

3.1.3.5 atomic包

在多线程(不是goroutine)编程中,如何保证多线程安全且性能够好呢?

下图例子是实现一个多线程计数器,使用atomic而不是锁能提升性能

image-20220523172554409

3.2 性能调优实战

3.2.1 性能调优简介

依靠数据而不是猜测

优化最大瓶颈而不是细枝末节

不要过早过度优化

3.2.2 性能分析工具pprof实战

pprof是可视化的分析性能和分析数据的工具,可以分析应用在什么地方耗费了多少CPU、多少Memory、goroutine阻塞的位置、互斥锁分析

3.2.2.1 pprof功能简介

image-20220523182244903

3.2.2.2 pprof排查实战

blog:github.com/eddycjy/blo…,pprof简介

code:github.com/wolfogre/go…,pprof项目实战

blog:blog.wolfogre.com/posts/go-pp…,pprof项目实战

操作步骤:

①在goland中运行上面code中的main.go

②浏览器打开网址 http://localhost:6060/debug/pprof/ ,页面如下

image-20220523213936409 ​ 页面上展示了可用的程序运行采样数据,下面也有简单说明,分别是: ​ allocs:内存分配情况 ​ block:阻塞操作情况 ​ cmdline:程序启动命令及参数 ​ goroutine:当前所有goroutine的堆栈信息 ​ heap:堆上内存使用情况(同alloc) ​ mutex:锁竞争操作情况 ​ profile:CPU占用情况 ​ threadcreate:当前所有创建的系统线程的堆栈信息 ​ trace:程序运行跟踪信息

在浏览器点击每个标签,展示的采样数据可读性很差,我们需要借助 go tool pprof 命令来阅读数据,这个命令是 go 原生自带的,所以不用额外安装

我们只需关注①CPU(profile)、②内存(heap)、③协程(goroutine)、④锁(mutex)、⑤阻塞(block)

1、分析CPU占用过高的问题,在cmd或者git bash输入以下命令,即可采集10s内的数据,并进入pprof环境下

go tool pprof "http://localhost:6060/debug/pprof/profile?seconds=10"

image-20220523215108848

在pprof环境下输入topN(N指展示占用最多资源的前N个,默认是10个),即可查看占用CPU最高的函数

top

image-20220523220401446

image-20220523220459981

不难发现tiger.Eat()占用了最多的资源,因为找到定位后我们可以去优化它。某些行Flat=Cum说明该函数中没有调用其他函数,某些行Flat=0说明该函数中只调用了其他函数

在pprof环境下输入list Eat,便可查看Eat这个函数每一行执行时占用的CPU时间

list Eat

image-20220523222418751

在pprof环境下输入web(前提是已安装Graphviz),便能自动在浏览器打开,可视化地查看函数调用图以及占用资源情况

web

image-20220523224324012

最后,输入q退出pprof环境

q

2、分析内存占用过高的问题,操作和分析CPU一样,这里用一种别的方法来查看资源占用情况,会自动打开网站来查看 http://localhost:8080/ui/

go tool pprof -http=:8080 "http://localhost:6060/debug/pprof/heap"

image-20220523225810936

VIEW中可切换多种方式查看

3、分析协程使用情况,操作和分析CPU一样

go tool pprof -http=:8080 "http://localhost:6060/debug/pprof/goroutine"

4、分析锁的竞争情况,操作和分析CPU一样

go tool pprof -http=:8080 "http://localhost:6060/debug/pprof/mutex"

5、分析阻塞情况,操作和分析CPU一样

go tool pprof -http=:8080 "http://localhost:6060/debug/pprof/block"

3.2.2.3 pprof的采样过程和原理

留到面试前才看,现在看了也忘

3.2.3 性能调优案例

3.2.3.1 业务服务优化

建立服务性能评估手段,如benchmark

分析性能数据,定位性能瓶颈,如pprof

重点优化项改造

优化效果验证

3.2.3.2 基础库优化

3.2.3.3 Go语言优化

\