解读 2015 之 Golang 篇:Golang 的全迸发时代

阅读数:9398 2016 年 1 月 3 日

编者按:2015 年,整个 IT 技术领域发生了许多深刻而又复杂的变化,InfoQ 策划了“解读 2015”年终技术盘点系列文章,希望能够给读者清晰地梳理出技术领域在这一年的发展变化,回顾过去,继续前行。同时本文也是 Golang 专栏的开篇文章,以后会有更多 Golang 专栏的文章刊出。读者可参照专栏去深入 Go 语言,也可参照国内Go 项目汇总 Go 命令教程学习 Go 语言。并且可将对于本文或者对于 Golang 的问题,以评论的方式提交给我们,后期将会挑选一部分问题,出一篇答疑文章。

现今,21 世纪的第 2 个十年已经过半,互联网也真正进入了极速发展的阶段。在国内, 大家已经对“云计算”和“大数据”等名词耳熟能详了。在互联网软件开发领域,最主流或火爆的技术也无不与之有关。就拿 Golang(也可称为 Go 语言)来说,它就号称“云计算时代的 C 语言”。Go 语言在软件开发效率和运行效率之间做出了绝佳的权衡。这使得它既适应于互联网应用的快速开发,又能在高并发、高性能的开发场景中如鱼得水。在 2015 年,Go 语言在服务端程序开发和 Web 开发领域大放光彩的同时也成功介入到了移动端开发领域。也正因为此,越来越多的创业公司(尤其是互联网应用和云计算领域的创业公司)选择 Go 语言作为其技术栈的重要组成部分。在国内,据不完全统计,已在生产环境中使用 Go 语言程序的知名互联网公司已有百度、美团、360、京东、搜狐、豌豆荚、宜信、微影时代 等等,更不用说国内日渐增多的云计算创业公司了。由此可见,对于广大的互联网软件开发者而言,关注和学习 Go 语言已经是已经很有必要的事情了。

回顾

2016 年已经到来,距 Google 在 2012 年 3 月发布的 Go 1.0 已将近 4 年。在 2015 年,Go 语言发生了不小的变化。从该年初发布的 1.4 版本到该年 8 月下旬发布的 1.5 版本,Go 语言终于完成了自举的过程,即:几乎完全用 Go 语言程序重写了自己,仅留有少许汇编程序。Go 语言的自举非常彻底,包括了最核心的编译器、链接器、运行时系统等。显然,这是一个很有意义的过程,代表着能力和自信。与此同时,Go 语言的运行时性能得到了大幅提升,尤其是在 1.5 版本完成的并发 GC 使得 Go 语言程序在响应时间方面有了质的飞跃。另外,Go 语言所支持的操作系统和计算架构越来越多,几乎涵盖了现今主流甚至非主流的所有选项。

当然,改变不止如此。下面,我们就列举几个比较惊艳的改变,并稍加剖析。

并发的 GC

GC,一般认为是 garbage collector 的缩写形式,通常被译为垃圾回收器。不过,它有时候也被看做是 garbage collection 的缩写形式,中文译为垃圾回收。在下文中,当 GC 被当做动词用时,指的是垃圾回收。但当 GC 被当做名词用时,指的是垃圾回收器。

在 1.4 以及之前版本的 Go 语言中,每次 GC 都会导致完全的“stop the world”(也可称之为 STW)。这意味着在 GC 期间,Go 语言的运行时系统会让调度器暂停对已启用的 Goroutine 的一切调度。也就是说,任何未处于运行状态的 Goroutine 都不会被递交至内核线程和运行,直到当次 GC 完成。如此暂停的代价不容忽视,对于有高并发需求的程序来说有时会显得非常棘手。在 1.5 版本出来之前,Go 语言的 GC 也常常因此被开发者们诟病。

Go 1.5 的 GC 是一个非分代、无转移的采用标记 - 清扫算法和三色标记法的并发垃圾回收器。它大刀阔斧地利用各种手段大大缩减了STW 的时间。Go 语言官方保证,在50 毫秒的Go 程序运行时间中因GC 导致的调度停顿至多只有10 毫秒。有了这一保证,相当于为Go 程序设定了一个响应时间的上限。对于对响应时间敏感的程序(许多互联网程序都是如此),这绝对是一个重大利好。当然,如此质的飞跃并不是一蹴而就的。实际上,Go 1.4 也为此做了很多铺垫,比如对Goroutine 栈的改造以完全保证GC 的标记操作的准确无误。图1 展示了在最近几个版本的Go 语言中GC 的STW 时间与内存堆大小的对应关系。

图 1 GC Pause vs. Heap Size

总之,Go 1.5 为 Go 程序开启了全并发的时代。虽然 Go 语言官方说当前的 GC 还可以被进一步优化,但是笔者认为它已不再会成为 Go 程序性能的瓶颈。

自带标准工具的更新

Go 语言的亮点之一就是自带了很多标准工具以帮助开发人员方便地进行 Go 程序的检查、格式化、编译、测试、部署,甚至升级。这些工具已经涵盖了一个软件的生命周期的方方面面,极大的方便了 Go 程序的开发者们。在 1.4 版本中,Go 语言的标准工具集中加入了 go generate。顾名思义,这是一个用于生成 Go 语言代码的命令。有意思的是,这源于一个几乎所有的计算机程序研发者们都有过的梦想——让计算机程序自己编写程序。go generate 命令可以利用 YACC(Yet Another Compiler Compiler,一种编译器的生成器)并根据某种描述文件来生成 Go 语言代码。不过,千万不要被这句话吓到。即使我们不懂 YACC ,甚至对 Go 语言的 AST(Abstract Syntax Tree,译作抽象语法树)一无所知,也可以使用 go generate 命令。比如,我们可以利用 go generate 命令把一些 HTML(Hypertext Markup Language,译作超文本标记语言)页面模板文件内置到生成的 Go 程序代码文件中(顺便说一句,Go 语言有自己的 HTML 页面模板语法,可用于编写 HTML 页面模板)。这样就无需在部署用于 Web 站点的 Go 程序时携带那些额外的文件了。下面展示一小段用于实现此功能的代码:

程序员们应该可以猜到最后一行代码实际上是一行注释。实际上,只要有了这行注释(其中的 tpl_loader 是笔者写的一个小工具,也非常的简单易懂),再在当前代码包目录下运行 go generate 命令,就可以实现上述功能了。在 Go 语言官方博客中可看到更详细的说明。

在 2015 年里,Go 语言在标准工具方面的增进还不止于此。一个更加令人兴奋的标准工具——go tool trace——随着 1.5 版本的发布而到来。Go 程序开发者们可以利用这一工具来图形化的展示出 Go 程序的追踪文件。当然,在展示之前,我们先要通过某种方式生成这样的文件。Go 语言为我们提供了三种用于生成 Go 程序追踪文件的方法:通过显式调用指定的标准库函数以手动生成、导入指定的标准库代码包以使其自动生成,以及在运行程序测试时添加指定标记来生成。此后,当我们运行 go tool trace 命令并把相应的可执行的 Go 程序文件和 Go 程序追踪文件作为参数以后,就可以在你的默认 Web 浏览器中看到类似下图的图形化展示了。

图 2 Go 程序追踪文件的图形化展示

如图所示,Go 程序追踪文件可以呈现出对应 Go 程序的内存使用、Goroutine 和内核线程状态、调度过程等信息。为我们调试 Go 并发程序提供了非常有力的辅助。

除了上述两个新增的标准工具之外,Go 语言官方也对一些已有的标准工具做了改进,比如:为 go build 命令和 go test 命令新增了可用标记以使其更加灵活、增强了 go tool vet 命令和 go doc 命令的功能,等等。

访问控制的增强

Go 语言对代码包的访问控制的进一步增强始于 1.4 版本。在 1.4 版本之前,Go 语言对程序实体(包括变量、常量、类型声明、函数等)采取的是两级访问控制策略。程序实体的名称的第一个字母的大小写决定了它的可访问范围。若为大写,则该程序实体可以被存在于任何位置的代码访问到。我们称这样的程序实体是公开的。若为小写,则该程序实体仅能被存在于当前代码包的代码访问到。我们称这样的程序实体是包级私有的。这样简明扼要的规则非常容易被记住。

到了 Go 1.4 时代,第三种访问控制规则出现。官方称之为 Internalpackages。其含义是,如果代码包 A 直接包含了一个名为 internal 的子代码包,那么这个 internal 包中公开的程序实体仅能被存在于代码包 A 及其子代码包中的代码访问到。这相当于使 Go 程序代码有了“模块级”的访问控制。

不过,在 Go 1.4 中,这个“模块级”的访问控制只是对 Go 语言源码和标准库中的(即 <Go 语言安装目录 >/src 中的)代码包有效,而对于其它代码包则是无效的。也就是说,这是一个过渡阶段。为的是让广大 Go 程序开发者有个适应期。到了 Go 1.5,由 internal 代码包代表的“模块级私有”访问控制规则才对所有代码包有了强制约束力。至此,Go 语言拥有了一套完整的三层访问控制策略。相比于之前的两层访问控制策略,它更加灵活和完善。

跨平台编译

在 1.5 版本之前,我们要想实现 Go 语言程序的跨平台编译是相当困难的。虽然因此催生出了几个开源的辅助工具,但其步骤也依然是相当繁琐的。其最主要的原因是那时的 Go 语言编译工具是由 C 语言编写的,是平台相关的。这里的平台相关,是指被编译后的程序的运行必要条件包含了目标计算机(也就是用于运行该程序的那台计算机)的操作系统和计算架构。其中,操作系统的可选项有 windows、linux、darwin 等,而计算架构的可选项目前有 386(即 32 位计算架构)、amd64(即 64 位计算架构)和 arm(一种基于精简指令集的计算架构,多用于便携设备专用 CPU)。例如,我们在 32 位的 Windows 操作系统下,使用平台相关的 Go 语言编译工具编译的程序是不能在 64 位的 Linux 操作系统下运行的,反之亦然。

我们已经知道,Go 1.5 的所有编译工具和运行时系统都被用 Go 语言重写了。这使得这些编译工具被进行了一系列合并,并且跨越了平台的界线。例如,之前针对不同计算架构的 Go 语言编辑器 5g、6g 和 8g 被合而为一并命名为 compile。随之而来的优势就是,我们可以使用这些编译工具轻而易举的进行跨平台的程序编译操作。我们只需在程序编译操作之前设置一下目标计算的操作系统和计算架构。前者通过设置环境变量 GOOS 来实现,而后者通过设置环境变量 GOARCH 来实现。例如,当我在 64 位的 Linux 操作系统下通过执行如下命令来编译一个 Go 源码文件之后,就可以在 32 位的 Windows 操作系统下直接运行那个编译后的结果文件了。

对 Android 和 iOS 开发的支持

从 Go 1.4 开始,开发人员已经可以利用官方扩展库中的 mobile 代码包来编写 Android App 了。到了 Go 1.5,其对 iOS App 的支持也已可用。这无疑是一大里程碑,使人振奋!它使得 Go 语言的适用领域大大拓展,并垒砌了其在移动互联网时代甚至物联网时代的根基。开发者们既可以使用 Go 语言来构建原生的 Android App 或 iOS App,也可以用它来编写可被 App 程序调用的基础库。读者可以通过官方文档来了解相关步骤,也可以去看看Go 语言的创始人之一Rob Pike 为大家编写的示例App 。另外,大家也可以关注Go 语言北京用户组和北京GDG( Google Developer Group - Beijing)联合主办的Go 语言技术聚会,其中会包括与此有关的Topic。此聚会的时间初定在2016 年2 月的下旬。

其他调整

除上述比较突出的变化之外,Go 语言在很多地方也做了调整。比如,Goroutine 内存栈的增长方式的变更,Goroutine 内存栈的初始大小由 8K 缩减为了 2K、GOMAXPROCS 的默认值由 1 变成与当前计算机的 CPU 核心数一致、Go 代码可以被用于生成动态链接库了,等等。对于这些调整,笔者就不一一细说了。不过,它们对于 Go 语言在 2015 年的精进也都起到了一定的推动作用。

总之,Go 语言在 2015 年的发展迅速且振奋人心。无论在其本身的功能、性能和适用领域上,还是在社区方面(尤其是在中国)都是如此。如果说笔者在著《Go 并发编程实战》这本书的时候还只是建议大家把 Go 语言作为自己的第一或第二编程语言并以此作为长线技术投资的话,那么现在我强烈建议所有互联网软件开发者都去尝试并使用 Go 语言构建他们的(个人或公司的)软件系统,并真正将其作为手边的常用工具。

Go 语言号称是云计算时代的 C 语言。它也正在持续、快速地向着这一目标前进。如果你也打算跟上后云计算时代、物联网时代以及不久就会出现的人工智能时代的话,那么就很有必要玩儿转 Go 语言了。我相信,它一定不会让你失望。

展望

在 2016 年,我们目前可见的 Go 语言进展就是预计在 2 月份发布的 1.6 版本了。在这个版本中,Go 语言官方准备重点发展 UI 库。这也是 Go 语言当前的一大短板。在官方的 UI 库完全就绪之前,笔者希望大家去关注一下七叶(优秀的国产 Go 语言 IDE——LiteIDE——的作者)的新项目 GoQt 。此项目就是一个基于 QT 的 Go 语言 UI 库。鉴于 LiteIDE 的优秀,我非常看好这个项目。另外,Go 语言在程序测试支持、程序运行分析以及程序调试方面都会有所改进,尤其是后者。实际上,许多不适应使用 Go 语言开发程序的程序员的最大抱怨就是 Go 语言程序不易调试(不过大家可以去了解下真正的 Go 语言爱好者是怎样调试 Go 程序的)。Go 语言官方也在 2015 年的重大版本升级中对这一方面做出了很多改进。随之而来的就是 Go 语言在于各大主流 IDE 的集成方面所作出的不断努力。在 Go 1.6,这种努力还会继续。程序的开发效率与运行效率同样重要。甚至在某些时候,前者比后者更加重要。这也是许多脚本语言得以生存并繁荣发展的重要原因之一。Go 语言的创造者们更是深谙此道。最后,Go 语言还会在移动 App 开发方面进行一步的增强。笔者相信 Go 语言在这一开发领域一定会有长足的进步的。

在奇点临近的当下,由于各种智能移动设备的先天优势,移动开发需求持续暴涨。我想,大多数互联网软件开发团队(尤其是创业团队)都会想用尽量精简的技术栈去支持多方产品的需求。如果能用一种既可快速上手又能高效运维的技术那该有多好。如果你通读本篇并有所思考,就很可能有与我相同的感受——Go 语言就是这样的一门技术。如果你觉得理由并不充分或想深入了解 Go 语言,那么就加入到 Go 语言社区并切实的感受一下吧。笔者发起的 Go 语言北京用户组(微信公众号:golang-beijing)也欢迎你的加入。

在不久的将来,Go 语言一定会实现在程序开发领域的全面覆盖。到那时,Go 程序员的含金量也就毋庸置疑了。那么,我们为什么不现在就去做如此重要的技术投资或去积极的在内部培养 Go 开发工程师呢?

作者简介:郝林,Go 语言北京用户组的发起人,著有图灵原创图书《Go 并发编程实战》,同时也是在线免费教程《Go 命令教程》和《Go 语言第一课》的作者。现在微赛时代担任平台研发负责人。

收藏

评论

微博

发表评论

注册/登录 InfoQ 发表评论