这是我参与「第三届青训营 -后端场」笔记创作活动的的第3篇笔记。
课程导学链接
【Go 语言原理与实践学习资料(上)】第三届字节跳动青训营-后端专场 - 掘金
课程项目地址
课程PPT链接
课程主要分为两个部分,分别是高质量编程和性能调优实战。其中高质量编程多是一些指导性原则,辅以一些示例进行讲解,加上课程PPT以及导学链接中已经总结得很清晰完善,所以本篇笔记仅将其中总结性内容以及个人不太熟悉的点列出,不详细讲解每一个细节。
这节课程承接前两节Go语言基础和Go语言工程实践,可以看出青训营培训的体系性还是很不错的,课程内容也非常充实,每节课PPT都有80-100页,很多知识点课上无法详细讲解。推荐大家按照自身学习状态结合推荐链接以及一些高质量学习资源进行自学,相关实践项目多动手。
高质量编程
编码规范
-
代码格式
使用gofmt格式化代码,使用goimports工具进行包导入管理(可以自动导入包,去除不适用的包,并按字典序排列,节省时间)
-
注释
- 代码是最好的注释
- 注释应该提供代码未表达出的上下文信息
-
命名规范
- 核心目标是降低阅读理解代码的成本
- 重点考虑上下文信息,设计简洁清晰的名称
-
控制流程
- 线性原理,处理逻辑尽量走直线,避免复杂的嵌套分支
- 正常流程代码沿着屏幕向下移动
- 提升代码可维护性和可读性
- 故障问题多出在复杂的循环语句和条件语句中
-
错误和异常处理
- error尽可能提供简明的上下文信息链,方便定位问题
- panic用于真正异常的情况
- recover生效范围,在当前goroutine的被defer函数中生效
关于编码规范其实很多点都需要在实际的开发过程中去体会规范为什么这么设计以及一些具体的Best Practice是如何应用的,课堂上已经总结了不少,这里推荐阅读Go代码Review建议,它可以看作effective go的一个补充。下面我列出一些review建议中比较简单实用但是可能一开始不会注意到的内容
- 用完整的句子写注释方便自动化展示,句子开头为所注释的东西,多句的话第一句作为摘要
- 不要引用其他包的内存(防御式编程)
- 使用
crypto/rand替代math/rand,更加安全 - 错误信息不大写,不要句号,因为错误信息一般都被包括在上下文中
- 接口放在使用方的package中而不是实现方的package,简单来说在接口只有在被使用的地方才定义
- 行的长度应该根据语义而定,不要死守80字符的“规矩”,现在的显示设备已经很先进了,但是一样要避免过长的行
- go中返回值可以有名字,但是要注意不要添加冗余的名字,例如
err error就毫无意义,如果有多个同类型返回值加个名字会好很多 - 不要用
this,me,self给某个类型方法的接收方对象命名(就像这样func (this Hello) hello())
性能优化建议
- 使用Benchmark来衡量性能表现
- Slice预分配内存以减少内存申请,提高运行速度
- Slice使用copy替代re-slice来减少内存占用,节省内存空间
- Map预分配内存
- 字符串处理使用strings.Builder
- 空结构体节约内存,利用其语义特征,同时不耗费资源(利用map实现set)
- atomic包原子化操作提高性能
上面提到的许多建议在这本Go语言高性能编程中都有所涉及,感兴趣可以仔细阅读。
性能调优实战
课程中的讲解示例其实是基于下面这篇博客做的,所以如果课上没有跟着老师做下来的话课后可以看一遍这个博客,博客中讲的很详细适合实操。
💡 注意这里很多同学运行此项目都出了一些问题,下面给出解决方案。
首先直接从GitHub中clone下来的项目是没有go.mod文件的,而go1.18强制使用module来管理项目,所以需要我们自己生成go.mod文件,具体运行以下命令
$ go mod init github.com/wolfogre/go-pprof-practice
$ go mod tidy
复制代码
会生成如下的go.mod文件
module github.com/wolfogre/go-pprof-practice
go 1.18
复制代码
可以看到是没有外部依赖的。
其次需要注意这里的module名字不能修改,只能是github.com/wolfogre/go-pprof-practice,如果你使用了其他的名字,例如go-pprof-practice,则会生成这样的go.mod文件
module go-pprof-practice
go 1.18
require github.com/wolfogre/go-pprof-practice v0.0.0-20190402114113-8ce266a210ee
复制代码
虽然程序也能运行,但是依赖的却是GitHub上的仓库(go mod tidy会将这个包拉下来存到你的gopath里面),你修改你本地tiger.go文件中的内容,main函数的运行结果是不会变的。
课程中还讲解了实际业务性能调优的案例,不过我还未接触过相关实际业务,对此方面完全没有概念,所以仅作了解。
课后问题
导学链接中提供了一些课后小问题,简单回答一下。
-
了解下其他语言的编码规范,是否和 Go 语言编码规范有相通之处,注重理解哪些共同点
曾阅读过Google C++ 风格指南,其实整体上的思想是差不多的,核心都是让代码自注释,注释起到一些补充功能,都非常注重简洁统一的风格,始终记住代码是写给人看的,让人能够更好地理解代码,从而降低与自己、与他人合作的难度。
区别在于不同的语言有一些自身的特性,例如go语言提供了defer方便释放资源,所以任何资源获取时defer一下就很方便,C++提供了完整的面向对象的封装,用起来对于成员和函数权限控制更好。
-
编码规范或者性能优化建议大部分是通用的,有没有方式能够自动化对代码进行检测?
go vet和revive(替代golint)等,在vscode中使用golangci-lint可以配置同时使用多个检查工具,具体可在设置页面搜索lint,然后配置如下,这样保存时就会自动进行检测以及format。
-
从 github.com/golang/go/t… 中选择感兴趣的包,看看官方代码是如何编写的
自行查看,一般一个包的代码非常多,全看下来不容易,可以挑一两个感兴趣的函数看看,然后看看整体的包的结构,重点放在编码的规范上。
-
使用 Go 进行并发编程时有哪些性能陷阱或者优化手段?
简单给出两个链接
-
在真实的线上环境中,每个场景或者服务遇到的性能问题也是各种各样,搜索下知名公司的官方公众号或者博客,里面有哪些性能优化的案例?比如 eng.uber.com/category/os…
前两天刚好看到了字节技术团队的文章,改进了Go的排序算法,并将要被纳入Go runtime中。
-
Go 语言本身在持续更新迭代,每个版本在性能上有哪些重要的优化点?
在Go语言官方博客中查看release note,这里列出1.15至1.18的优化点