精通一门编程语言
Go在成为下一门企业编程语言的轨道上
摘要
Go(一种用于大规模软件开发的编程语言)提供了强大的开发经验,避免了现有编程语言所存在的许多问题。 这些因素使它成为将来成功取代Java成为主导企业软件平台的最有可能的候选人之一。 强烈建议在未来几十年内寻求安全且具有前瞻性的技术选择以创建大规模云基础架构的公司和开源计划将Go视为其主要编程语言。 Go的优势在于:
- 基于实际经验
- 专注于大型工程
- 注重可维护性
- 保持简单明了
- 使事情变得清晰明了
- 很容易学
- 提供一种做事的方式
- 允许轻松的内置并发
- 提供面向计算的语言原语
- 使用OO-好的部分
- 拥有现代化的标准库
- 实施标准化格式
- 有一个非常快的编译器
- 使交叉编译容易
- 执行非常快
- 需要很小的内存空间
- 导致部署规模较小
- 完全独立地部署
- 供应商依赖性
- 提供兼容性保证
- 鼓励好的文档
- 建立为商业支持的开源
继续阅读以获取有关每个点的更多详细信息。 但是,在提交Go之前,您应该注意以下事项:
- 不成熟的图书馆
- 即将发生的变化
- 没有硬实时
介绍
Go是Google开发的一种编程语言,在最近几年中取得了很多成功。 现代云,网络和DevOps软件的很大一部分都是用Go编写的,例如Docker , Kubernetes , Terraform , etcd或ist.io。 许多公司也将其用于通用开发。 Go启用的功能使这些项目能够吸引大量用户,而Go的易用性带来了许多贡献。
Go的优势在于结合了简单可靠的想法,同时避免了其他语言中发现的许多问题。 这篇博客文章概述了Go背后的一些设计原理和工程智慧,以展示它们如何(合在一起)使它成为成为下一代大型软件开发平台的优秀候选人。 许多语言在各个方面都比较强大,但是在将所有语言组合在一起时,没有其他语言的分数能始终如一地出色,尤其是在大规模软件工程方面。
基于实际经验
Go是由经验丰富的软件行业资深人士创建的,他们在很长一段时间内都已感受到现有语言的缺点所带来的痛苦。 几十年前,Rob Pike和Ken Thompson在Unix,C和Unicode部分发明中发挥了重要作用。 在使用JavaScript和Java的V8和HotSpot虚拟机之后,Robert Griesemer在编译器和垃圾回收方面拥有数十年的经验。 由于不得不等待Google大小的C ++ / Java代码库编译太多次,他们开始创建另一种编程语言,其中包含了半个世纪的编写代码所学的一切。
专注于大型工程
小型工程项目几乎可以使用任何编程语言来成功构建。 当数以千计的开发人员在数十年来持续不断的时间压力下,在包含数千万行代码的海量代码基础上进行协作时,就会发生真正的痛苦问题。 这将导致出现以下问题:
- 长时间的编译会中断开发
- 该代码库已由多个人员/团队/部门/公司拥有,混合了不同的编程样式
- 该公司拥有成千上万的工程师,架构师,测试人员,Ops专家,审核员,实习生等,他们需要了解代码基础,但拥有广泛的编码经验
- 依赖于许多外部库或运行时,其中一些不再以其原始形式存在
- 在代码库的整个生命周期中,每行代码平均被重写了10次 ,从而留下了疤痕,疣和技术漂移
- 不完整的文件
Go专注于减轻这些大规模的工程难题 ,有时以使工程规模较小的工作为代价,例如,在这里和那里需要一些额外的代码行。
注重可维护性
Go强调将尽可能多的工作分流到自动代码维护工具上。 Go工具链提供最常用的功能,例如格式化代码和导入,查找符号的定义和用法,简单的重构以及识别代码的味道。 归功于标准化的代码格式和一种惯用的处理方式,机器生成的代码更改看起来非常类似于Go中人类生成的更改,并使用类似的模式,从而使人机之间的无缝协作成为可能。
保持简单明了
初级程序员为简单的问题创建简单的解决方案。 高级程序员为复杂的问题创建复杂的解决方案。 优秀的程序员可以找到解决复杂问题的简单解决方案。 - 查尔斯·康奈尔
许多人惊讶于Go中没有其他语言所爱的概念。 Go确实是一种非常小而简单的语言,仅包含很少量的正交和经过验证的概念。 这鼓励开发人员以最少的认知开销编写尽可能简单的代码,以便许多其他人可以理解和使用它。
使事物显式和显而易见
好的代码是显而易见的,避免了聪明,模糊的语言功能,扭曲的控制流和间接寻址。
许多语言专注于提高编写代码的效率。 但是,在其生命周期中,人们花在阅读代码上的时间将比最初编写代码所需的时间多得多(100倍)。 示例是查看,理解,调试,更改,重构或重用它。 在查看代码时,通常只看到并理解其中的一小部分,通常没有完整的整个代码库的完整概述。 为了解决这个问题,Go将所有内容都明确了。
一个例子是错误处理。 只让异常在各个点中断代码并使调用链冒泡会更容易。 Go需要手动处理或返回每个错误 。 这样可以明确指出可以在何处中断代码以及如何处理或包装错误。 总的来说,这使错误处理更加麻烦,但更易于理解。
简单易学
Go非常小而简单,因此只需几天就可以研究整个语言及其基本概念。 根据我们的经验,经过不到一周的培训(相比于其他语言,则为数月),Go初学者可以理解Go专家编写的代码并做出贡献。 为了使增加人数变得容易,Go网站提供了所需的所有教程和深入的文章。 这些教程在浏览器中运行,使人们甚至可以将Go语言安装到本地计算机上,然后学习和玩Go。
一种做事的方式
Go使团队合作胜过个人的自我表达。
在Go(和Python)中,所有语言功能都是正交且彼此互补的,通常有一种做某事的方法。 如果您要求10个Python或Go程序员解决问题,您将获得10个相对类似的解决方案。 不同的程序员在彼此的代码库中感到更自在。 查看其他人的代码时, 每分钟的WTF会更少,并且人们的工作会更好地融合在一起,从而形成一个大家都为之骄傲并喜欢工作的一致的整体。 这样可以避免大规模的工程问题,例如:
- 开发人员视良好的工作代码为“混乱”,并要求对其进行重写,因为他们与原始作者的想法不同。
- 不同的团队成员用语言的不同子集编写同一代码库的各个部分。
简单的内置并发
Go专为现代多核硬件而设计。
当今使用的大多数编程语言(Java,JavaScript,Python,Ruby,C,C ++)都是在1980年代至2000年代设计的,当时大多数CPU只有一个计算核心。 这就是为什么它们本质上是单线程的,并且将并行化作为针对极端情况的事后考虑,通过诸如线程和同步点之类的插件来实现,这些插件繁琐且难以正确使用。 第三方库提供了诸如Actor模型之类的更简单的并发形式,但是总是有多个可用选项,从而导致语言生态系统的碎片化。 当今的硬件具有越来越多的计算核心,必须并行化软件才能在其上高效运行。 Go是在多核CPU时代编写的,并且在语言中内置了简单的高级CSP风格的并发。
面向计算的语言基元
从根本上说,计算机系统接收数据,对数据进行按摩(通常分几步进行),然后输出结果数据。 例如,Web服务器从客户端接收HTTP请求,并将其转换为一系列数据库或后端调用。 这些调用返回后,它将接收到的数据转换为HTML或JSON并将其输出到调用方。 Go的内置语言原语直接支持此范例:
- 结构代表数据
- 读者和作家代表流媒体IO
- 功能处理数据
- goroutine提供(几乎无限)并发
- 在并发处理步骤之间传送管道数据
由于该语言以直接形式提供了所有计算原语,因此Go源代码表示服务器直接执行的操作。
OO —好零件
面向对象非常有用。 使用它的最后几十年取得了丰硕的成果,并为我们提供了有关它的哪些部分可扩展性更好的见解。 考虑到这些学习,Go在面向对象方面采取了一种新方法。 它保留了诸如封装和消息传递之类的好部分。 Go避免了继承,因为它现在被认为是有害的,并为合成提供了一流的支持 。
现代标准图书馆
当前使用的许多编程语言(Java,JavaScript,Python,Ruby)是在Internet成为当今无处不在的计算平台之前进行设计的。 因此,这些语言的标准库仅对未针对现代Internet优化的网络提供相对通用的支持。 Go创建于十年前,当时互联网已经如火如荼。 Go的标准库允许在没有第三方库的情况下甚至创建复杂的网络服务。 这样可以避免第三方库的常见问题:
- 碎片化:实现相同功能的总是有多种选择
- 膨胀:库经常实现的功能超出了它们的用途
- 依赖地狱:库通常依赖于特定版本的其他库
- 质量未知:第三方代码的质量和安全性可疑
- 未知的支持:第三方库的开发可以随时停止
- 意外的变化:第三方库的版本通常不如标准库严格
拉斯·考克斯(Russ Cox)对此提供了更多背景信息。
标准化格式
Gofmt的风格没有人喜欢,但gofmt却是每个人的最爱。 —罗伯·派克
Gofmt是一种以标准化方式格式化Go代码的程序。 这不是最漂亮的格式化方式,而是最简单,最不令人讨厌的格式化方式。 标准化的源代码格式具有令人惊讶的积极效果:
- 将对话重点放在重要主题上:它消除了围绕标签与空格,缩进深度,行长,空行,大括号的放置等问题的整个自行车棚辩论。
- 开发人员在彼此的代码库中会感到宾至如归,因为其他代码看起来很像他们会编写的代码。 每个人都喜欢拥有以自己喜欢的方式格式化代码的自由,但是如果其他人以他们喜欢的方式自由格式化代码,那么每个人都讨厌它。
- 自动代码更改不会破坏手写代码的格式,例如通过引入意外的空白更改。
许多其他语言社区现在正在开发gofmt等效项。 作为第三方解决方案构建时,通常会存在几种相互竞争的格式标准。 例如,JavaScript世界提供了Prettier和StandardJS 。 一个可以一起使用,也可以同时使用。 许多JS项目都不采用它们,因为这是一个额外的决定。 Go的格式化程序已内置在该语言的标准工具链中,因此只有一个标准,每个人都在使用它。
快速编译
大型代码库的漫长编译时间是引发Go起源的一大笔钱。 Google主要使用C ++和Java,与Haskell,Scala或Rust等更复杂的语言相比,它们的编译速度相对较快。 尽管如此,在编译大型代码库时,即使是少量的慢速操作也会激怒和扰乱编译延迟。 Go从头开始设计,以提高编译效率,因此,其编译器是如此之快,以至于几乎没有编译延迟。 这为Go开发人员提供了类似于脚本语言的即时反馈,并具有静态类型检查的其他优点。
交叉编译
由于语言运行时非常简单,因此已将其移植到许多平台,例如macOS,Linux,Windows,BSD,ARM等。 Go可以立即为所有这些平台编译二进制文件。 这使得从单台机器进行部署变得容易。
快速执行
Go的运行速度接近C的速度。与JIT语言(Java,JavaScript,Python等)不同,Go二进制文件无需启动或预热时间,因为它们作为已编译和完全优化的本机代码提供。 Go垃圾收集器仅引入微不足道的微秒级暂停。 除了快速的单核性能外,Go还使所有CPU内核的使用变得容易 。
小内存占用
诸如JVM,Python或Node之类的运行时不仅会在运行程序时加载程序代码。 它们还会加载大型且高度复杂的基础架构,以在每次运行程序时对其进行编译和优化。 这会使它们的启动时间变慢,并使它们使用大量(数百MB)的RAM。 Go流程的开销较小,因为它们已经过完全编译和优化,只需要运行即可。 Go还以内存高效的方式存储数据 。 这在内存有限且昂贵的云环境中以及在开发过程中非常重要,在开发环境中,我们希望在单个计算机上快速启动整个堆栈,同时为其他软件保留内存。
部署规模小
Go二进制文件的大小非常简洁。 Go应用程序的Docker映像通常比用Java或Node编写的映像小10倍以上,因为它不需要包含编译器,JIT和更少的运行时基础结构。 在部署大型应用程序时,这很重要。 想象将一个简单的应用程序部署到100个生产服务器上。 使用Node / JVM时,我们的Docker注册表必须提供100个Docker映像@ 200 MB = 20 GB。 这需要花费一些时间。 想象我们要每天部署100次。 使用Go服务时,Docker注册表仅需提供100个Docker映像@ 20 MB = 2 GB。 大型Go应用程序可以更快,更频繁地部署,从而使重要的更新更快地投入生产。
独立部署
Go应用程序被部署为包含所有依赖项的单个可执行文件。 无需安装特定版本的JVM,Node或Python运行时。 无需将库下载到生产服务器上。 无需对运行Go二进制文件的计算机进行任何更改。 甚至没有必要将Go二进制文件包装到Docker中以共享它们。 您只需将Go二进制文件拖放到服务器上,无论该服务器上正在运行的其他文件如何,二进制文件都将在其中运行。 上述声明的唯一例外是在使用net和os / user软件包时针对glibc的动态链接。
供应商依存关系
Go有意避免使用第三方库的中央存储库。 Go应用程序直接链接到相应的Git存储库,并将所有相关代码下载(“供应商”)到各自的代码库中。 这具有许多优点:
- 我们可以在使用第三方代码之前对其进行检查,分析和测试。 该代码与我们自己的代码一样,也是我们应用程序的一部分,应符合相同的质量,安全性和可靠性标准。
- 无需永久访问存储依赖项的各个位置。 一次从任何地方(包括私有Git仓库)获取您的第三方库,您将永远拥有它们。
- 签出后,无需进一步下载依赖项即可编译代码库。
- 如果Internet上的某个代码存储库突然提供了不同的代码,也就不足为奇了。
- 即使软件包存储库变慢或托管软件包不存在,部署也不会中断
兼容性保证
Go团队承诺 ,现有程序将继续在新一代语言中运行。 这样就可以轻松地将大型项目升级到较新版本的编译器,并受益于它们带来的许多性能和安全性改进。 同时,由于Go二进制文件包含了所需的所有依赖关系,因此可以在同一台服务器上并排运行使用不同版本的Go编译器编译的二进制文件,而无需进行复杂的设置。多个版本的运行时或虚拟化。
文献资料
在进行大型工程设计时,文档对于使软件可访问和可维护很重要。 与其他功能类似,Go中的文档非常简单实用:
- 它嵌入在源代码中,因此可以同时维护两者。
- 它不需要特殊的语法-文档只是普通的源代码注释。
- 可运行的单元测试通常是文档的最佳形式,因此Go让您将其嵌入到文档中 。
- 所有文档实用程序都内置在工具链中,因此每个人都可以使用它们。
- Go linter需要导出元素的文档,以防止“文档债务”的累积。
商业支持的开源
商业实体在公开场合发展时,就会出现一些最受欢迎且经过精心设计的软件。 此设置结合了商业软件开发的优势(一致性和完善性,使系统健壮,可靠和高性能)与开放式开发的优势,如跨多个行业的广泛支持,多个大型实体和许多用户的支持以及长期的即使停止商业支持也能提供支持。 Go是通过这种方式开发的。
缺点
当然,Go并非完美无缺,每种技术选择总会有优缺点。 这是在选择Go之前要考虑的一小部分区域。
不成熟
尽管Go的标准库在支持许多新概念(例如HTTP 2服务器推送)方面处于行业领先地位,但与JVM生态系统中现有的第三方库相比,用于外部API的第三方Go库可能不那么成熟。
即将发生的变化
Go团队知道几乎不可能更改现有语言元素,因此小心谨慎,只有在新功能完全开发后才能添加。 经过有意的10年稳定性阶段之后,Go团队正在考虑对语言进行一系列较大的改进 ,这是Go 2.0之旅的一部分。
没有硬实时
尽管Go的垃圾收集器仅引入了非常短的中断,但要支持硬实时性,就需要无垃圾收集的技术,例如Rust。
结论
这篇博客文章为Go的设计提供了一些明智但通常不是那么明显的选择的背景知识,以及随着代码库和团队数量级的增长,如何使大型工程项目免于繁琐的工作。 总体而言,它们将Go定位为寻求Java以外的现代编程语言的大型开发项目的绝佳选择。
精通一门编程语言