Golang 始于 2007 年,2009 年 11 月正式开源,2012 发布了 Go 1 稳定版本。
创始人:
后来还加入了 Ian Lance Taylor、Russ Cox 等人。这些计算机科学领城的重量级人物设计 Golang 的初衷是满足 Google 的需求:在不损失应用程序性能的情况下降低代码的复杂性,具有 “部署简单、并发性好、语言设计良好、执行性能好” 等优势。
计算机软件经历了数十年的发展,形成了多种学术流派,例如:面向过程编程、面向对象编程、函数式编程、面向消息编程等。此外,近年来也出现了一些小众的编程哲学。
Golang 对这些思想亦有所吸收。例如:Golang 接受了函数式编程的一些想法,支持匿名函数与闭包;Golang 接受了以 Erlang 语言为代表的面向消息编程思想,支持 Goroutine 和 Channel,并推荐使用消息而不是共享内存来进行并发编程。
在 Golang 出现之前,开发者们总是面临非常艰难的抉择,究竟是使用执行速度快但是编译速度并不理想的语言(如:C++),还是使用编译速度较快但执行效率不佳的语言(如:.NET、Java),或者说开发难度较低但执行速度一般的动态语言(如:Python)呢?
在 Google I/O 2012 的 Go 设计小组见面会上,Rob Pike 是这样说的:“我们做了大量的 C++ 开发,厌烦了等待编译完成,尽管这是玩笑,但在很大程度上来说也是事实。”
显然,Go 语言在这 3 个条件之间做到了最佳的平衡:快速编译,高效执行,易于开发。
Go Gopher,这是才华横溢的插画家 Renee French 设计的,她也是 Go 设计者之一 Rob Pike 的妻子。
Golang 的语法规则严谨,没有歧义,更没什么黑魔法变异用法。任何人写出的代码都基本一致,这使得 Golang 简单易学。放弃部分 “灵活” 和 “自由”,换来更好的维护性。
将 ++ 、-- 从运算符降级为语句,虽然保留了指针,但默认阻止指针运算。将切片和字典作为内置类型,从运行时的层面进行优化,这也算是一种 “简单”。
Golang 是在多核和网络化的时代背景下诞生的原生支持并发的编程语言。Golang 从底层原生支持并发,无须第三方库,开发人员可以很轻松地在编写程序时决定怎么使用 CPU 资源。
Golang 的并发基于 Goroutine,Goroutine 类似于线程,但并非线程。可以将 Goroutine 理解为一种虚拟线程。Golang 运行时会参与调度 Goroutine,并将 Goroutine 合理地分配到每个 CPU 中,最大限度地使用 CPU 性能。
可以说,Goroutine 是 Go 最显著的特征。它用 “类协程” 的方式来处理并发单元,却又在运行时层面做了更深度的优化处理。这使得语法上的并发编程变得极为容易,无须处理回调,无须关注线程切换,仅需使用一个 go
关键字,简单而自然。
多个 Goroutine 中,Golang 使用 Channel(通道)进行通信,通道是一种内置的数据结构,可以让用户在不同的 Goroutine 之间同步发送具有类型的消息。这让编程模型更倾向于在 Goroutine 之间发送消息,而不是让多个 Goroutine 争夺同一个数据的使用权。
Goroutine 搭配 Channel,实现 CSP 模型。将并发单元间的数据耦合拆解开来,各司其职,这对所有纠结于内存共享、锁粒度的开发人员都是一个可期盼的解脱。若说有所不足,那就是应该有个更大的计划,将通信从进程内拓展到进程外,实现真正意义上的分布式。
程序可以将需要并发的环节设计为生产者模式和消费者的模式,将数据放入通道。通道另外一端的代码将这些数据进行并发计算并返回结果,如下图所示:
Go 选择了 tcmalloc 内存分配器,它本就是为并发而设计的高性能内存分配组件。
可以说,内存分配器是运行时三大组件里变化最少的部分。刨去因配合垃圾回收器而修改的内容,内存分配器完整保留了 tcmalloc 的原始架构。使用 cache 为当前执行线程提供无锁分配,多个 central 在不同线程间平衡内存单元复用。在更高层次里,heap 则管理着大块内存,用以切分成不同等级的复用内存块。快速分配和二级内存平衡机制,让内存分配器能优秀地完成高压力下的内存管理任务。
在最近几个版本中,编译器优化卓有成效。它会竭力将对象分配在栈上,以降低垃圾回收压力,减少管理消耗,提升执行性能。可以说,除偶尔因性能问题而被迫采用对象池和自主内存管理外,我们基本无须参与内存管理操作。
在垃圾回收方面,Go 面临了很多困难。因指针的存在,所以回收内存不能做收缩处理。幸好,指针运算被阻止,否则要做到精确回收都难。
每次升级,垃圾回收器必然是核心组件里修改最多的部分。从并发清理,到降低 STW 时间,直到 Go 的 1.5 版本实现并发标记,逐步引入三色标记和写屏障等等,都是为了能让垃圾回收在不影响用户逻辑的情况下更好地工作。尽管有了努力,当前版本的垃圾回收算法也只能说堪用,离好用尚有不少距离。
静态编译的好处显而易见。将运行时、依赖库直接打包到可执行文件内部,简化了部署和发布操作,无须事先安装运行环境和下载诸多第三方库。这种简单方式对于编写系统软件有着极大好处,因为库依赖一直都是个麻烦。
Golang 支持交叉编译,可以在 Linux 操作系统上开发运行于 Windows 的应用程序。
这是第一门完全支持 UTF-8 的编程语言,这不仅体现在它可以处理使用 UTF-8 编码的字符串,就连它的源码文件格式都是使用的 UTF-8 编码。Go 语言做到了真正的国际化!
功能完善、质量可靠的标准库为编程语言提供了充足动力。在不借助第三方扩展的情况下,就可完成大部分基础功能开发,这大大降低了学习和使用成本。最关键的是,标准库有升级和修复保障,还能从运行时获得深层次优化的便利,这是第三方库所不具备的。
Go 标准库虽称不得完全覆盖,但也算极为丰富。其中值得称道的是 net/http,仅须简单几条语句就能实现一个高性能 Web Server,这从来都是宣传的亮点。更何况大批基于此的优秀第三方 Framework 更是将 Go 推到 Web/Microservice 开发标准之一的位置。
当然,优秀第三方资源也是语言生态圈的重要组成部分。
完整的工具链对于日常开发极为重要。Go 在此做得相当不错,无论是编译、格式化、错误检查、帮助文档,还是第三方包下载、更新都有对应的工具。其功能未必完善,但起码算得上简单易用。
内置完整测试框架,其中包括单元测试、性能测试、代码覆盖率、数据竞争,以及用来调优的 pprof,这些都是保障代码能正确而稳定运行的必备利器。
除此之外,还可通过环境变量输出运行时监控信息,尤其是垃圾回收和并发调度跟踪,可进一步帮助我们改进算法,获得更佳的运行期表现。
根据 Go 开发团队和基本的算法测试,Golang 与 C 语言的性能差距大概在 10%~20% 之间。
这里以国外的一个编程语言性能测试网站 http://benchmarksgame.alioth.debian.org/ 为测试基准和数据源。这个网站可以对常见的编程语言进行性能比较,网站使用都是最新的语言版本和常见的一些算法。
通过对 C(GCC)、C++、Java、JavaScript 和 Golang 的测试。性能比较如下表所示,表中数据的单位为秒,数值越小表明运行性能越好。
编程语言:
云原生:
Web Server: