这是我参与「第五届青训营」伴学笔记创作活动的第 3 天。
如何编写更简洁清晰的代码
原则:保证简单性,可读性,生产力。
公共约定部分:①包中声明的每个公共的符号:
②变量、常量、函数以及结构都需要添加注释
③任何既不明显也不简单的公共功能必须予以注释
④无论长度或复杂程度如何对库中的任何函数都必须进行注释。
⑤不需要注释实现接口的方法.
然后老师推荐使用gofmt自动格式化代码来进行代码规范的限制,goland在编码结束的时候会进行自动格式化,goimports在gofmt的基础上加上依赖包的管理,自动增删依赖的包引用、将依赖包按字母顺序排序并进行分类。
代码的注释内容:代码作用,如何做的,实现的原因,什么情况会出错。
示例
命名规范:
缩略词全大写,但当其位于变量开头且不需要导出时,使用全小写
变量距离其被使用的地方越远,需要携带越多的上下文信息。
函数名不需要携带包名的上下文信息,简短,当名为foo的包某个函数返回类型为Foo时,可以省略类型信息而不导致歧义,返回类型是T(非Foo)可以加入类型信息。
包名:小写字母,简短且包含一定的上下文信息,不要与标准库同名。不用常用变量,使用单数,缩写慎用。
控制流程:优先处理错误情况/特殊情况,尽早返回或继续循环来减少嵌套。尽量走直线,避免复杂的嵌套分支。
、
错误和异常处理:
错误判定,使用errors.Is,使用这个方法可以判定错误链上的所有错误是否含有特定的错误。错误链获取特定种类的错误,使用errors.As。panic不建议在业务代码中使用,用于真正异常的情况。使用库抛出panic影响自身逻辑,使用recover
如果需要更多的上下文信息,可以recover后在log中记录当前的调用栈debug.stack()
Benchmark工具执行命令:go test -bench=.-benchmem
sice预分配内存:data:=make([]int,0)和data:=make([]int,0,size)执行时间三分之一,内存分配次数少。然后在已有切片基础上创建切片不会创建新的底层数组,即原来的数组等不到释放,可以使用copy代替re-slice(go test -run=.-v测试)
map:data:=make(map[int]int)和data:=make(map[int]int,0,size)
字符串处理:①+;每次都是重新分配内存②builder.WriteString()时间最短,直接将底层的字符数组转换成字符串类型返回;③byte.Buffer重新申请了一块空间。
空结构体struct{}实例不占据任何的内存空间:实现Set
多线程编程,atomic的包(优先)/加锁
锁是操作系统实现,属于系统调用,atomic是通过硬件实现,sync.Mutex应该用来保护一段逻辑,不仅仅用于保护一个变量,对于非数值操作,可以使用atomic.Value,能承载一个interface{}
性能调优原则:要依靠数据而不是猜测,要定位最大瓶颈而不是细枝末节,不要过早/过度优化。
pprof:希望知道应用在什么地方耗费了多少CPU、Memory以及pprof是用于可视化和分析性能分析数据的工具。
top命令,flat:当前函数本身的执行耗时,flat%:flat 占 CPU 总时间的比例,sum%:上面每一行的 flat% 总和,cum:指当前函数本身加上其调用函数的总耗时,cum%:cum 占 CPU 总时间的比例。Flat==Cum:函数中没有调用其他函数,Flat==0:函数中只有其他函数的调用。通过top查找到最消耗资源的函数,然后list Eat:查看问题,定位到实际的代码。
web命令:调用关系可视化。找到代码之后注释然后编译运行。
查看内存:
alloc_objects: 程序累计申请的对象数alloc_space: 程序累计申请的内存大小
inuse_objects: 程序当前持有的对象数inuse_space: 程序当前占用的内存大小
goroutine泄露也会导致内存泄露,查看goroutine的话只需要更改后缀。
goroutine-协程
由上到下表示调用顺序;每一块代表一个函数,越长代表占用 CPU 的时间更长;火焰图是动态的,支持点击块进行分析
锁muter 阻塞block 有过滤条件,所以block影响小的话就进行过滤。首页的block进去可以查看全部block
操作系统:每10ms向进程发送一次SIGPROF信号
进程:每次接收到SIGPROF会记录调用堆栈
写缓冲:每100ms读取已经记录的调用栈并写入输出流
Heap-堆内存
采样程序通过内存分配器在堆上分配和释放的内存,记录分配/释放的大小和数量
采样率:每分配512KB记录一次,可在运行开头修改,1为每次分配均记录
采样时间:从程序运行开始到采样时
采样指标: alloc_space, alloc objects,inuse space,inuse_objects
计算方式: inuse = alloc - free
profiler:分析器
服务:能单独部署,承载一定功能的程序。
依赖:a的功能实现依赖b的响应成果,a依赖b。
调用链路:能支持一个接口请求的相关服务集合及其相互之间的依赖关系
基础库:公共的工具包,中间件
流程:建立服务性能评估手段,分析性能数据,定位性能瓶颈,重点优化项改造,优化效果验证
单独:
评估手段:
服务性能评估方式,请求流量构造,压测范围,性能数据采集。压测结果的分析
从火焰图定位代码,可查更新缓存json,log日志,高并发场景的CPU的占用性能
把线上请求的返回值记录下来,然后修改后的返回值记录下来,然后查看差异,差异不大就是功能影响不大
修改之后重复压测验证,然后上线评估优化效果,关注服务监控,逐步放量,收集性能数据据积累经验。
链路:
小数据集能否就满足?是否实时需要,能不能加缓存,重复请求的请求是否能合并。
AB实验SDK的优化
①分析基础库核心逻辑和性能瓶颈
②设计完善改造方案
③数据按需获取
④数据序列化协议优化
⑤内部压测验证
⑥推广业务服务落地验证
Go语言本身的优化,优化内存分配策略,优化代码编译流程,生成更高效的程序,内部压测验证,推广业务服务落地验证。优点:接入简单,只需要调整编译配置,通用性强。