Go 简介

admin
admin 2020年02月07日
  • 在其它设备中阅读本文章

一、由来

Go 语言 亦叫 Golang 语言,是 Google 开发的一种 静态强类型 编译型 并发型 ,并具有 垃圾回收 功能的编程语言。于 2007 年 9 月开始设计,并在 2009 年 11 月正式宣布推出。Go 语言的主要开发者有:肯. 汤姆逊 (Ken Thompson)、罗布. 派克(Rob Pike) 和罗伯特. 格里泽默 (Robert Griesemer)。

  • 肯. 汤姆逊 (Ken Thompson):图灵奖得主,Uinx 发明人,B 语言作者(C 语言前身),还做飞行员,后来被谷歌挖走
  • 罗布. 派克 (Rob Pike):Unix 团队和 Plan 9 操作系统计划的成员,与 Ken 老爷子共事多年,并共创出广泛使用的 UTF- 8 字元编码
  • 罗伯特. 格里泽默 (Robert Griesemer):曾协助制作 Java 的 HotSpot 编译器,和 Chrome 浏览器的 JavaScript 引擎 V8

Go 语言开发者自述,近 10 多年,从单机时代的 C 语言到现在互联网时代的 Java,都没有令人满意的开发语言,而 C++ 往往给人的感觉是,花了 100% 的经历,却只有 60% 的开发效率,产出比太低,Java 和 C#的哲学又来源于 C ++。并且,随着硬件的不断升级,这些语言不能充分的利用硬件及 CPU。因此,一门高效、简洁、开源的语言诞生了。

二、特性

1、语法简单

Go 语言的语法规则严谨,没有歧义,更没什么类似 C 语言的黑魔法变异用法。任何人写出的代码都基本一致,这使得 Go 语言简单易学。放弃部分“灵活”和“自由”,换来更好的维护性。
将“++”、“--”从运算符降级为语句,保留指针,但默认阻止指针运算,带来的好处是显而易见的。还有,将切片和字典作为内置类型,从运行时的层面进行优化,这也算是一种“简单”。

2、并发模型

时至今日,并发编程已成为程序员的基本技能,在各个技术社区都能看到诸多与之相关的讨论主题。在这种情况下 Go 语言却一反常态做了件极大胆的事,从根本上将一切都并发化,运行时用 Goroutine 运行所有的一切,包括 main.main 入口函数。
可以说,Goroutine 是 Go 最显著的特征。它用类协程的方式来处理并发单元,却又在运行时层面做了更深度的优化处理。这使得语法上的并发编程变得极为容易,无须处理回调,无须关注线程切换,仅一个关键字,简单而自然。
搭配 channel,实现 CSP 模型。将并发单元间的数据耦合拆解开来,各司其职,这对所有纠结于内存共享、锁粒度的开发人员都是一个可期盼的解脱。若说有所不足,那就是应该有个更大的计划,将通信从进程内拓展到进程外,实现真正意义上的分布式。

3、高性能

Go 语言和 C 语言都是编译型语言,根据 Go 开发团队和基本的算法测试,Go 语言与 C 语言的性能差距大概在 10%~20% 之间。虽然没有官方的性能标准,但是与其它各个语言相比已经拥有非常出色的表现。
时下流行的语言大都是运行在虚拟机上,如:Java 和 Scala 使用的 JVM,C# 和 VB.NET 使用的.NET CLR。尽管虚拟机的性能已经有了很大的提升,但任何使用 JIT 编译器和脚本语言解释器的编程语言(Ruby、Python、Perl 和 JavaScript)在 C 和 C ++ 的绝对优势下甚至都无法在性能上望其项背。

4、函数多返回值

在 C 中如果想返回多个值,通常会在调用函数中分配返回值的空间,并将返回值的指针传给被调函数。Go 的做法是在传入的参数之上留了两个空位,被调者直接将返回值放在这两空位。Go 是使用栈空间来返回值的。而常见的 C 语言是通过寄存器来返回值的。
大多数语言只能返回一个值,Go 语言可以返回多个值。这个功能使得开发者再不用绞尽脑汁的想到底怎么返回值的设计,也不用为了传值专门定义一个结构体。

5、强制代码风格

自增操作符不再是一个操作符,而是一个语句,因此,在 Go 语言中自增只有一种写法:i++;左括号必须紧接着语句不换行,其他样式的括号将被视为代码编译错误……这些特性刚开始会使开发者有一些不习惯,但随着对 Go 语言的不断熟悉,开发者就会发现风格统一让大家在阅读代码时把注意力集中到了解决问题上,而不是代码风格上。
同时 Go 语言也提供了一套格式化工具。一些 Go 语言的开发环境或者编辑器在保存时,都会使用格式化工具对代码进行格式化,让代码提交时已经是统一格式的代码。

6、垃圾回收

垃圾回收一直是个难题。早年间,Java 就因垃圾回收低效被嘲笑了许久,后来 Sun 连续收纳了好多人和技术才发展到今天。可即便如此,在 Hadoop 等大内存应用场景下,垃圾回收依旧捉襟见肘、步履维艰。
相比 Java,Go 面临的困难要更多。因指针的存在,所以回收内存不能做收缩处理。幸好,指针运算被阻止,否则要做到精确回收都难。
每次升级,垃圾回收器必然是核心组件里修改最多的部分。从并发清理,到降低 STW(停止程序回收内存)时间,直到 Go 的 1.5 版本实现并发标记,逐步引入三色标记和写屏障等等,都是为了能让垃圾回收在不影响用户逻辑的情况下更好地工作。尽管有了努力,当前版本的垃圾回收算法也只能说堪用,离好用尚有不少距离。

9、与 c 语言交互

在 Go 语言中可以直接重用了大部份的 C 模块,这里称为 Cgo。Cgo 允许开发者混合编写 C 语言代码,然后 Cgo 工具可以将这些混合的 C 代码提取并生成对于 C 功能的调用包装代码。开发者基本上可以完全忽略这个 Go 语言和 C 语言的边界是如何跨越的。

8、标准库

功能完善、质量可靠的标准库为编程语言提供了充足动力。在不借助第三方扩展的情况下,就可完成大部分基础功能开发,这大大降低了学习和使用成本。最关键的是,标准库有升级和修复保障,还能从运行时获得深层次优化的便利,这是第三方库所不具备的。
包含 I / O 操作、文本处理、图像、密码学、网络和分布式应用程序等,并支持许多标准化的文件格式和编解码协议。Go 标准库虽称不得完全覆盖,但也算极为丰富。其中值得称道的是 net/http,仅须简单几条语句就能实现一个高性能 Web Server,这从来都是宣传的亮点。

9、工具链

完整的工具链对于日常开发极为重要。Go 在此做得相当不错,无论是编译、格式化、错误检查、帮助文档,还是第三方包下载、更新都有对应的工具。其功能未必完善,但起码算得上简单易用。
内置完整测试框架,其中包括单元测试、性能测试、代码覆盖率、数据竞争,以及用来调优的 pprof,这些都是保障代码能正确而稳定运行的必备利器。
除此之外,还可通过环境变量输出运行时监控信息,尤其是垃圾回收和并发调度跟踪,可进一步帮助我们改进算法,获得更佳的运行期表现。

三、为并发而生

在早期 CPU 都是以单核的形式顺序执行机器指令。Go 语言的祖先 C 语言正是这种顺序编程语言的代表。顺序编程语言中的顺序是指:所有的指令都是以串行的方式执行,在相同的时刻有且仅有一个 CPU 在顺序执行程序的指令。
随着处理器技术的发展,单核时代以提升处理器频率来提高运行效率的方式遇到了瓶颈,单核 CPU 发展的停滞,给多核 CPU 的发展带来了机遇。相应地,编程语言也开始逐步向并行化的方向发展。
虽然一些编程语言的框架在不断地提高多核资源使用效率,例如 Java 的 Netty 等,但仍然需要开发人员花费大量的时间和精力搞懂这些框架的运行原理后才能熟练掌握。
作为程序员,要开发出能充分利用硬件资源的应用程序是一件很难的事情。现代计算机都拥有多个核,但是大部分编程语言都没有有效的工具让程序可以轻易利用这些资源。编程时需要写大量的线程同步代码来利用多个核,很容易导致错误。
Go 语言正是在多核和网络化的时代背景下诞生的原生支持并发的编程语言。Go 语言从底层原生支持并发,无须第三方库,开发人员可以很轻松地在编写程序时决定怎么使用 CPU 资源。
Go 语言的并发是基于 goroutine 的,goroutine 类似于线程,但并非线程。可以将 goroutine 理解为一种虚拟线程。Go 语言运行时会参与调度 goroutine,并将 goroutine 合理地分配到每个 CPU 中,最大限度地使用 CPU 性能。
多个 goroutine 中,Go 语言使用通道(channel)进行通信,通道是一种内置的数据结构,可以让用户在不同的 goroutine 之间同步发送具有类型的消息。这让编程模型更倾向于在 goroutine 之间发送消息,而不是让多个 goroutine 争夺同一个数据的使用权。

四、与其它编程语言对比

在软件行业做过一段时间的人都知道,没有万能的编程语言,也没有万能开发框架,更没有万能的解决方案。任何新技术的产生都应该归功于一部分人对老旧技术的强烈不满。Go 语言也不例外。比如,C 语言的依赖管理、C++ 的垃圾回收、Java 笨重的类型系统和厚重的 Java EE 规范,以及脚本语言(如 PHP、Python 和 Ruby)的性能,这些都是很多开发者社区经常争论和抱怨的问题。

Go 语言的优势
Go 语言是集多编程范式之大成者,体现了优秀的软件工程思想和原则,其特性可以使开发者快速地开发、测试和部署程序,大大提高了生产效率。下面我们来看看与其他主流语言相比,Go 语言具有的优势。

  • 相对于 C /C++ 来讲,Go 语言拥有清晰的依赖管理和全自动的垃圾回收机制,因此其代码量大大降低,开发效率大大提高。
  • 相对于 Java 来讲,Go 语言拥有简明的类型系统、函数式编程范式和先进的并发编程模型。因此其代码块更小更简洁、可重用性更高,并可在多核计算环境下更快地运行。
  • 对于 PHP 来讲,Go 语言更具通用性和规范性。这使得其更适合构建大型的软件,并能够更好地将各个模块组织在一起。在性能方面,PHP 不可与 Go 同日而语。
  • 对于 Python/Ruby 来讲,Go 的优势在于其简洁的语法、非侵入式和扁平化的类型系统和浑然天成的多范式编程模型。与 PHP 一样,Python 和 Ruby 也是动态类型的解释型语言,这就意味着它们的运行速度会比静态类型的编译型语言慢很多。
    总而言之,Go 语言对于当前大多数主流语言来讲,最大的优势在于具有较高的生产效率、先进的依赖管理和类型系统,以及原生的并发计算支持。因此,Go 语言自发布以来就受到了各个领域开发者的关注和青睐。

Go 语言的劣势
下面,我们来客观地看一下目前 Go 语言需要加强或改进的地方。

  • 从分布式计算的角度来看,Go 语言的成熟度不及 Erlang(现在已经出现了一些这方面的 Go 语言代码包,我们已经可以看到光明的未来了)。
  • 从程序运行速度的角度来看,Go 语言虽然已与 Java 不相上下,但还不及 C(差距正在不断地缩小)。
  • 从第三方库的角度来看,Go 语言的库数量还远远不及其他几门主流语言(比如 Java、Python、Ruby 等)。不过与 Go 语言的年纪相比,用它实现的第三方库已经相当多了,并且它们的数量在持续地飞速增长中。

另外,在更深的层面,Go 语言标准库中也有些不尽如人意的的地方,具体如下。

  • 从语言语法角度来看,Go 语言语法里的语法糖并不多,这让许多 Python、Ruby 爱好者们对它不屑一顾。另外,变量赋值方式多得有点儿累赘了。最让人遗憾的也是我比较在意的一个地方是,Go 语言不支持自定义的泛型类型。
  • 从并发编程角度来看,Go 语言提供的并发模型很强大,但也有一些编写规则需要了解。否则,很容易踩进“坑”里。其实不提倡把这叫作“坑”。因为这些所谓的“坑”,大都是我们由于对原理不熟悉而自己挖出来的。
  • 从垃圾回收角度看,Go 语言的垃圾回收采用的是并发的标记清除算法(Concurrent Mark and Sweep,CMS)。虽然是并发的操作,时间比串行操作短很多,但是还是会在垃圾回收期间停止所有用户程序的操作。这一点多少会影响到对实时性要求比较高的应用。不过,在 Go 语言 1.3 之后的版本中,这方面的问题已经得到了极大的改善。

虽然 Go 语言还有一些瑕疵,但从整体来看,它已经是一门非常优秀的通用编程语言了。并且,Go 语言在今后的发展上会关注性能、可靠性、可移植性和一些功能增强,所以上述缺憾会随着版本的推进而逐渐减弱和消失。

五、适合用途

其实 Go 语言主要用作服务器端开发,其定位是用来开发“大型软件”的,适合于需要很多程序员一起开发,并且开发周期较长的大型软件和支持云计算的网络服务。
Go 语言融合了传统编译型语言的高效性和脚本语言的易用性和富于表达性,不仅提高了项目的开发速度,而且后期维护起来也非常轻松。

鉴于 Go 语言的特点和设计的初衷,从以下几个方面来分析 Go 语言擅长的领域:
在服务器编程方面,Go 语言适合处理日志、数据打包、虚拟机处理、文件系统、分布式系统、数据库代理等;
网络编程方面,Go 语言广泛应用于 Web 应用、API 应用、下载应用等;
此外,Go 语言还可用于开发底层应用、内存数据库和云平台领域,目前国外很多云平台都是采用 Go 开发。

下面列举了一些基于 Go 语言开发的优秀开源项目:
云计算基础设施领域,代表项目:docker、kubernetes、etcd、consul、cloudflare CDN、七牛云存储等。
基础软件,代表项目:tidb、influxdb、cockroachdb 等。
微服务,代表项目:go-kit、micro、monzo bank 的 typhon、bilibili 等。
互联网基础设施,代表项目:以太坊、hyperledger 等。

总之,Go 语言的优势还是比较多的,比如 Go 语言的性能非常出色,最关键的是在性能强劲的同时还能像解释型语言一样高效地进行开发。