2015年,整个IT技术领域发生了许多深刻而又复杂的变化,InfoQ策划了“解读2015”年终技术盘点系列文章,希望能够给读者清晰地梳理出技术领域在这一年的发展变化,回顾过去,继续前行。读者可参照专栏去深入Go语言,也可参照国内Go项目汇总和Go命令教程学习Go语言。 现今,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语言源码和标准库中的(即 |
|
声明:文章版权归原作者所有 部分文章转自互联网 如有侵权请联系
[邮箱地址] 删除
|