购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

第2条
选择适当的Go语言版本

了解Go语言的版本发布历史以及不同版本的主要变动点,有助于程序员根据自身实际情况选择最合适的Go版本。

2.1 Go语言的先祖

和绝大多数编程语言相似, Go语言也是“站在巨人的肩膀上的” ,正如图2-1所示,Go继承了诸多编程语言的特性。

图2-1 Go语言的先祖(图片来自《Go程序设计语言》一书)

Go的基本语法参考了C语言,Go是“C家族语言”的一个分支;而Go的声明语法、包概念则受到了Pascal、Modula、Oberon的启发;一些并发的思想则来自受到Tony Hoare教授CSP理论 [1] 影响的编程语言,比如Newsqueak和Limbo。

[1] https://www.cs.cmu.edu/~crary/819-f09/Hoare78.pdf

2.2 Go语言的版本发布历史

2009年11月10日,Go语言正式对外发布并开源。之后,Go语言在一段时间内采用了Weekly Release的模式,即每周发布一个版本。目前我们在Go语言的GitHub官方仓库中仍能找到早期的Weekly Release版本,比如weekly.2009-11-06。Go语言的版本发布历史如下。

■从2011年3月7日开始,除了Weekly Release,Go项目还会每月发布一次,即Monthly Release,比如release.r56,这种情况一直延续到Go 1.0版本发布之前。

■2012年3月28日,Go 1.0正式发布。同时Go官方发布了“Go1兼容性”承诺:只要符合Go1语言规范的源代码,Go编译器将保证向后兼容(backwards compatible),即使用新版编译器可以正确编译使用老版本语法编写的代码。

■ 2013年5月13日,Go 1.1版本发布,其主要的变动点如下。

●新增method value语法:允许将方法绑定到一个特定的方法接收者(receiver)实例上,从而形成一个函数(function)。

●引入“终止语句”(terminating statement)的概念。

●将int类型在64位平台上的内部表示的字节数升为8字节,即64比特。

●更为精确的堆垃圾回收器(Heap GC),尤其是针对32位平台。

■ 2013年12月1日,Go 1.2版本发布。从Go 1.2开始,Go开发组启动了以每6个月为一个发布周期的发布计划。Go 1.2版本的主要变动点包括:

●增加全切片表达式(Full slice expression):a[low: high: max];

●实现了在部分场景下 支持抢占的goroutine调度器 (利用在函数入口插入的调度器代码);

●go test新增-cover标志,用于计算测试覆盖率。

■ 2014年6月18日,Go 1.3版本发布,其主要的变动点包括:

●支持更多平台,如Solaris、Dragonfly、Plan 9和NaCl等;

●goroutine的栈模型从分段栈(segmented stack)改为了连续栈(contiguous stack), 改善Hot stack split问题

●更为精确的栈垃圾回收器(Stack GC)。

■ 2014年12月10日,Go 1.4版本发布。Go 1.4也是最后一个编译器和运行时由C语言实现的版本。其主要的变动点包括:

●新增for range x {...}形式的for-range语法;

●使用Go语言替换运行时的部分C语言实现,这使GC变得全面精确;

●由于连续栈的应用,goroutine的默认栈大小从8kB减小为2kB;

●增加internal package机制;

●增加canonical import path机制;

●新增go generate子命令,用于辅助实现代码生成;

●删除Go源码树中的src/pkg/xxx中的pkg这一级别,直接使用src/xxx。

■ 2015年8月19日,Go 1.5版本发布。Go 1.5是Go语言历史上的一个 具有里程碑意义的重要版本 。因为从这个版本开始, Go实现了自举 ,即无须再依赖C编译器。然而Go编译器的性能比Go 1.4的C实现有了较大幅度的下降。其主要的变动点包括:

●Go编译器和运行时全部使用Go重写,原先的C代码实现被彻底移除;

●跨平台编译Go程序更为简洁,只需设置两个环境变量——GOARCH和GOOS即可;

●支持map类型字面量(literal);

●GOMAXPROCS的初始默认值由1改为运行环境的CPU核数;

●大幅度优化GC延迟,在多数情况下GC停止世界(Stop The World)的时间短于10ms;

●增加vendor机制,改善Go包依赖管理;

●增加go tool trace子命令;

●go build增加-buildmode命令选项,支持将Go代码编译为共享库(shared library)的形式。

■ 2016年2月17日,Go 1.6版本发布,其主要的变动点包括:

●进一步优化GC延迟,实现Go程序在占用大内存的情况下,其GC延迟时间仍短于10ms;

●自动支持HTTP/2;

●定义了在C代码中共享Go指针的规则。

■ 2016年8月15日,Go 1.7版本发布,其主要的变动点包括:

●针对x86-64实现了SSA后端,使得编译出的二进制文件大小减小20%~30%,而运行效率提升5%~35%;

●Go编译器的性能比Go 1.6版本提升近一倍;

●go test支持subtests和sub-benchmarks;

●标准库新增context包。

■ 2017年2月16日,Go 1.8版本发布,其主要的变动点包括:

●支持在仅tags不同的两个struct之间进行显式类型转换;

●标准库增加sort.Slice函数;

●支持HTTP/2 Push机制;

●支持HTTP Server优雅退出;

●增加了对Mutex和RWMutex的profiling(剖析)支持;

●支持Go plugins,增加plugin包;

●支持默认的GOPATH路径($HOME/go),无须再显式设置;

●进一步优化SSA后端代码,程序平均性能提升10%左右;

●Go编译器性能进一步提升,平均编译链接的性能提升幅度在15%左右;

●GC的延迟进一步降低,GC停止世界(Stop The World)的时间通常不超过100μs,甚至不超过10μs;

●优化defer的实现,使得其性能损耗降低了一半。

■ 2017年8月25日,Go 1.9版本发布,其主要的变动点包括:

●新增了type alias语法;

●在原来支持包级别的并发编译的基础上实现了包函数级别的并发编译,使得Go编译性能有10%左右的提升;

●大内存对象分配性能得到显著提升;

●增加了对单调时钟(monotonic clock)的支持;

●提供了一个支持并发的Map类型——sync.Map;

●增加math/bits包,将高性能位操作实现收入标准库。

■ 2018年2月17日,Go 1.10版本发布,其主要的变动点包括:

●支持默认GOROOT,开发者无须显式设置GOROOT环境变量;

●增加GOTMPDIR环境变量;

●通过cache大幅提升构建和go test执行的性能,并基于源文件内容是否变化判定是否使用cache中的结果;

●支持Unicode 10.0版本。

■ 2018年8月25日,Go 1.11版本发布。Go 1.11是Russ Cox在GopherCon 2017大会上发表题为“Toward Go 2”的演讲之后的第一个Go版本,它与Go 1.5版本一样也是具有里程碑意义的版本,因为它引入了新的Go包管理机制: Go module 。Go module在Go 1.11中的落地为后续Go 2相关提议的渐进落地奠定了良好的基础。该版本主要的变动点包括:

●引入Go module,为Go包依赖管理提供了创新性的解决方案;

●引入对WebAssembly的支持,让Gopher可以使用Go语言来开发Web应用;

●为调试器增加了一个新的实验功能——允许在调试过程中动态调用Go函数。

■ 2019年2月25日,Go 1.12版本发布,其主要的变动点包括:

●对Go 1.11版本中增加的Go module机制做了进一步优化;

●增加对TLS 1.3的支持;

●优化了存在大量堆内存(heap)时GC清理环节(sweep)的性能,使得一次GC后的内存分配延迟得以改善;

●运行时更加积极地将释放的内存归还给操作系统,以应对大块内存分配无法重用已存在堆空间的问题;

●该版本中Build cache默认开启并成为必需功能。

■ 2019年9月4日,Go 1.13版本发布,其主要的变动点包括:

●增加以0b或0B开头的二进制数字字面量形式(如0b111)。

●增加以0o或0O开头的八进制数字字面量形式(如0o700)。

●增加以0x或0X开头的十六进制浮点数字面量形式(如0x123.86p+2)。

●支持在数字字面量中通过数字分隔符“_”提高可读性(如a := 5_3_7)。

●取消了移位操作(>>和<<)的右操作数只能是无符号数的限制。

●继续对Go module机制进行优化,包括:当GO111MODULE=auto时,无论是在$GOPATH/src下还是$GOPATH之外的仓库中,只要目录下有go.mod,Go编译器就会使用Go module来管理依赖;GOPROXY支持配置多个代理,默认值为 https://proxy.golang.org,direct ;提供了GOPRIVATE变量,用于指示哪些仓库下的module是私有的,即既不需要通过GOPROXY下载,也不需要通过GOSUMDB去验证其校验和。

●Go错误处理改善:在标准库中增加errors.Is和errors.As函数来解决错误值(error value)的比较判定问题,增加errors.Unwrap函数来解决error的展开(unwrap)问题。

●defer性能提升30%。

●支持的Unicode标准从10.0版本升级到Unicode 11.0版本。

■ 2020年2月26日,Go 1.14版本发布,其主要的变动点包括:

●嵌入接口的方法集可重叠;

●基于系统信号机制实现了异步抢占式的goroutine调度;

●defer性能得以继续优化,理论上有30%的性能提升;

●Go module已经生产就绪,并支持subversion源码仓库;

●重新实现了运行时的timer;

●testing包的T和B类型都增加了自己的Cleanup方法。

■ 2020年8月12日,Go 1.15版本发布,其主要的变动点包括:

●GOPROXY环境变量支持以管道符为分隔符的代理值列表;

●module的本地缓存路径可通过GOMODCACHE环境变量配置;

●运行时优化,将小整数([0, 255])转换为interface类型值时将不会额外分配内存;

●支持更为现代化的新版链接器,在Go 1.15版本中,新版链接器的性能相比老版本可提高20%,内存占用减少30%;

●增加tzdata包,可以用于操作附加到二进制文件中的时区信息。

■ 2021年2月18日,Go 1.16版本发布,其主要的变动点包括:

●支持苹果的M1芯片(通过darwin/arm64组合);

Go module-aware模式成为默认构建模式 ,即GO111MODULE值默认为on;

●go build/run命令不再自动更新go.mod和go.sum文件;

●go.mod中增加retract指示符以支持指示作废module的某个特定版本;

●引入GOVCS环境变量,控制获取module源码所使用的版本控制工具;

●GODEBUG环境变量支持跟踪包init函数的消耗;

●Go链接器得到进一步的现代化改造,相比于Go 1.15版本的链接器,新链接器的性能有20%~25%的提升,资源占用下降5%~15%,更为直观的是,编译出的二进制文件大小下降10%以上;

●新增io/fs包,建立Go原生文件系统抽象;

新增embed包 ,作为在二进制文件中嵌入静态资源文件的官方方案。

2.3 Go语言的版本选择建议

如今,Go团队已经将版本发布节奏稳定在每年发布两次大版本上,一般是在2月和8月。Go团队承诺对最新的两个Go稳定大版本提供支持,比如目前最新版本是Go 1.16,那么Go团队会为Go 1.16和Go 1.15版本提供支持。如果Go 1.17版本发布,那么支持的版本将变成Go 1.17和Go 1.16。支持的范围主要包括修复版本中存在的重大问题、文档变更及安全问题更新等。

Go开发团队发布的Go语言稳定版本的平均质量一直是很高的,少有影响使用的重大bug。Go开发团队一直 建议大家使用最新的发布版 。Google的自有产品,比如Google App Engine(以下简称为GAE),也向来是这方面的“急先锋”——一般在Go新版本发布不久后,GAE便会宣布支持最新版本的Go。

开源社区对Go版本的选择策略并不相同。有的开源项目采纳了Go团队的建议,在Go最新版本发布不久就将当前项目的Go编译器版本升级到最新版,比如Kubernetes项目;而有的开源项目(如Docker项目)则比较谨慎,在Go开发团队发布Go 1.16版本之后,这些项目可能还在使用两个发布周期之前的版本(如Go 1.14);而多数项目处于两者之间,即使用最新版本之前的那个版本。比如:当前最新版本为Go 1.16,那么这些项目会使用Go 1.15版本的最新补丁版本(Go 1.15.x),直到发布Go 1.17,这些项目才会切换到Go 1.16的最新补丁版本(Go 1.16.x)。如果你不是那么“激进”,可以采用最后这种版本选择策略。

小结

在这一条中,我们简单回顾了Go语言自诞生至今所有重大版本的功能特性和版本意义。了解Go版本的演进情况以及不同版本的功能特性变动点,有助于我们在学习中或在项目开发中选择最适合自己的Go版本。此外,我们还了解了Go社区在Go版本选择和升级方面的几种策略,大家可以根据实际情况选择最适合自己的策略。 c9MAVkQhTvU59fSl7iMfg1YXf2HGOUpGKxRCOq/n5Kjtbot4gKTLbSpUv/QTxp+e

点击中间区域
呼出菜单
上一章
目录
下一章
×