本文首发于nanmu42,谢绝转载,感谢你对原创的支持。(鞠躬)

这篇文章是为已经有一些编程基础,打算将Golang作为第二编程语言进行学习的同学准备的,希望能够为你的学习提供一些方向。

Go语言的吉祥物是Gopher(地鼠),Go语言爱好者也会自称Gopher 图/Renee French

概述

Go语言问世于2009年,由Google支持,Robert Griesemer, Rob Pike和Ken Thompson三位计算机大牛设计,是一个静态强类型、编译型,跨平台,语言原语支持并发,具有垃圾收集功能的类C风格编程语言。

左起,Robert Griesemer(V8 Javascript engine, Java HotSpot VM), Rob Pike(UNIX, Plan9, UTF-8)和Ken Thompson(B, Unix, UTF-8, grep) 视频截图/Youtube: Google I/O 2012 - Meet the Go Team
Golang

Golang目前处于v1大版本,一般每6个月发布一个小版本(1.x),每隔数日到数周发布修复和安全更新版本(1.x.y),大版本范围内保证向前兼容,你可以安心升级。

Golang是一门相对稳定、约束、保守的语言,语言的特性很少,新特性的引入往往至少花费一年,因此不用担心学不动(滑稽)。

Golang是云原生的主场语言,这里列举一些应用广泛的项目:

国内外优秀团队广泛地使用Golang,例如七牛、Google、Cloudflare、微软、Paypal、DropBox、Twitter、Uber、Netflix、HashiCorp、阿里巴巴、腾讯、百度、PingCAP、拳头游戏。

学习路径

比较有效的学习方式是在理解语言基础后实际地写几个项目,做中学,学中做。

语言基础

IDE

常见的IDE选择有:

如果你没有在上面找到喜欢的IDE,官方wiki里有一份更全的列表。

依赖库和文档

Golang的标准库非常务实,大部分功能都可以仅依赖标准库实现。标准库的列表见这里。

典型常用的标准库:

github.com/nanmu42/gzip

一些常用第三方库:

大陆的开发者可以使用由七牛云提供的镜像服务以极大提升第三方库的拉取体验。

深入学习

最佳实践

语言风格

编程是一个创造可能性的过程,同时是一个消去可能性的过程。

Golang倡导的语言风格是追求简洁明确和秩序的:

  • 简洁明确:关键词少,API精练;
  • 秩序(约束):CSP, gofmt,静态分析。

消去可能性让答案变得明显,秩序可以提升团队整体产率。

gofmt/goimports

一些小问题容易引发大争论:使用空格还是tab?数组末尾的元素要添加逗号吗?函数的大括号需要先换行吗?……?

Golang具有完善的静态代码辅助和分析工具,gofmt/goimports就是一个很重要的例子。

在大多编程语言中,代码文本的格式都难以统一——每个人对格式的看法不一,缺乏统一格式的工具(或者工具提供了太多选项让每个人都有机会设定得不一样),软件工程一般都需要团队协作,格式的争论给团队风格统一和代码评审(想想那些因为空格回车或者逗号带来的git diff)带来了困难。

gofmt会格式化Golang代码,这个过程非常迅速,你可以放心地将其添加到你的IDE的保存时钩子(on-save hook)中。gofmt没有提供任何格式上的选项,只要团队中每个人都使用gofmt,每个人的代码格式都是一样的。

没人喜欢golang/gofmt的格式,但是人人都喜欢gofmt.

goimports是gofmt的增强版,格式化代码的同时对依赖进行排序,以保证代码格式上的更高一致性,实际生产中常用于替代gofmt.

静态分析

Golang的编译为代码的正确性提供了基本保障,但这还不够。

静态分析意味着在代码编译前找到错误、可疑问题或优化点。

Golang的自举意味者其标准库中提供了大量用于解析代码文本和AST语法树的工具,这为Golang代码静态分析的繁荣生态提供了可能。

golangci-linter是近几年崭露头角的静态分析新秀,它集成了大量主题的linter,例如Golang官方提供的检查代码中可疑错误的govet,检查变量(再)赋值后但没有使用的ineffassign,检查忘记处理错误的errcheck,提出代码简化意见的gosimple等等。

和gofmt/goimports一样,我推荐你将golangci-linter添加到你的IDE的保存时钩子(on-save hook)中,在你编码过程中,它会在第一时间第一现场发现问题并提醒你。

注释和文档

官方指南Effective Go中阐述了编写代码注释以生成文档的最佳实践。

文档并不是越多越好,下面这些位置可能适合写文档:

  • 依赖库(package)描述。
  • 公开的(导出的)函数、结构体及其方法、变量、常量。
  • 业务或算法复杂的过程。
  • 排期进行的工作(TODO)。

下面这些位置尤其适合写文档:

interface{}

下面这些位置可能不适合写文档:

  • 作用范围有限的私有(未导出)变量、结构体、方法;
  • 名称、入参、出参和它们的类型已经能明确表征其用途和非用途的函数和方法;
  • 一目了然的过程。

测试

测试可以侦测实现错误、回归错误和意外更改,提升交付效率和信心。如果你已经反复多次手工测试过某个特性,我推荐你考虑为它写个测试。

Golang原生支持测试,官方提供了清晰的教程和文档.

部分IDE(如goland和Visual Studio Code)可以快速地生成测试代码所需的格式文本,你可以善用这个特性以提升效率。

如果你的测试需要数据库或者外部资源,除了自己部署这些资源并固定运行测试的环境以外,你还可以考虑使用github.com/ory/dockertest/v3在测试时启动一个即用即丟的容器化资源实例以支撑测试,这可以明显降低测试代码对环境的依赖。

编译和部署

二进制

Golang支持在Linux、Windows和Mac上编译二进制,二进制可执行文件包含完整的Golang运行时和你的程序功能,拷贝到相同系统相同架构的设备中可直接执行。

根据你的代码规模和依赖多寡,二进制的大小一般会在数MB到数十MB之间变动。

如果你的代码中不含cgo,你还可以在上述任意一个平台交叉编译另外两个平台的二进制。像Gox这样的工具可以让你的交叉编译工作更容易,但是它不是必须的。如果你的代码中含有cgo而你确实需要交叉编译,你可以考察xgo能不能满足你的需求,它提供了二进制命令行工具和容器化的依赖环境来支撑交叉编译。

如果你的二进制中需要嵌入静态文件(比如将前端工程打包到二进制中),你可以考虑使用Go 1.16引入的特性embed.

容器化

由于Golang本身就产出二进制可执行文件,镜像可以做得非常地薄,甚至,这里提供一个参考的Dockerfile:

实际在生产中我推荐使用Alpine做底包,为调试和排错留个口子,同时也更容易管理时区信息和CA证书:

工程化

HTTP服务

我推荐使用github.com/gin-gonic/gin这个HTTP服务框架来处理路由、中间件、请求绑定和验证。这里有一个脚手架工程作为例子。

net/http
net/http

如果你确定你的服务负载极高,在依赖服务、IO、内存、GPU、CPU上很可能遇到瓶颈,我推荐你考虑做服务限流/熔断/goroutine池,同时考虑换用gnet承载你的业务,这么做的缺点是会让服务更加复杂。

这里有一个使用goroutine池以规避内存和IO压力的例子,它支撑的服务是这个娱乐性的动物书封面生成器。

命令行程序

github.com/spf13/cobra提供了一个二进制工具和一套组织代码的方式让你构建命令行程序的过程更加容易,它提供了解析命令行子命令和参数的便利方法。

其他

工具推荐

下面这些工具不是必须的,但是可以极大地提升你的编码幸福度:

资源推荐