Go语言高效编程:原理、可观测性与优化
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

前言

软件开发一直都是一个求真务实的领域,软件工程师追求的往往是极高的性能目标、需求与效率的平衡、优雅而高效的代码,以及易于阅读、扩展和维护的代码库。实现这些真的可能吗?

是的,笔者将通过本书展示如何实现这些!本书在某些方面是独树一帜的,因为它不是一本速成教程,而是一本关于编写高效且务实的软件的完整指南,涵盖了笔者在软件开发领域的所见所闻。

通过本书,你将学到很多关于Go语言编程以及如何对其进行优化的知识。不要被本书的书名所迷惑,虽然笔者使用Go语言作为示例语言来展示优化思维和可观测性模式,但本书的11章中有8章与编程语言无关。这些技术理念是相通的,可以用来改进任何其他语言(例如,Java、C#、Scala、Python、C++、Rust或Haskell)编写的软件。

如果你期待的是一本包含大量底层优化技巧的书,那么这本书并不适合你。首先,软件的性能优化并不能一概而论。某人通过展开循环或在其结构体字段中使用指针获得了更好的效率,并不意味着可以推广到所有场景!本书将介绍一些优化技巧,但会着重强调效率的重要性。

其次,一些“低级”且有风险的技巧通常并没有多大用处。在大多数情况下,只需了解程序中浪费CPU时间和资源的症结所在,就可以低成本、高效率地实现效率和可扩展性目标。此外,你将了解到,除了用C++、Rust或汇编语言重写程序,在原程序基础上也存在高效的解决方案!

在阅读本书正文之前,建议先阅读本书的前言,其中有本书的写作目的,以及笔者专注于效率这个主题的原因。你还可以学习到如何更好地使用本书,从而在软件开发任务中受益。

缘何而作

笔者花了大约1200小时撰写本书,撰写本书并非一时冲动。在社交媒体盛行的时代,阅读与写作纸质书给人一种过时的感觉,但以笔者的经验来看,现代媒体往往将信息过度简化,因为它们全靠短平快的内容来吸引读者,并获得收益,因此读者看过之后会觉得似是而非。这会导致不良诱导(https://oreil.ly/A8dCv),且与笔者想通过本书实现的目标相冲突。

笔者的目标其实很简单——让自己所使用或依赖的软件变得更好,并实现以下目的:

• 希望软件的贡献者(Contributor)和维护者(Maintainer)能够知晓其代码的效率,通过持续优化让它变得更加高效。

• 希望软件的贡献者和维护者能够审慎对待所有的拉取请求(pull request),并提高效率。

• 希望周围的人知道如何专业地处理性能问题,而不是营造紧张氛围。

• 希望用户和利益相关者对目前行业中风靡的基准测试和廉价营销保持谨慎之心。

• 希望领导者、主管和产品经理能够充分地处理软件效率问题,并合理制定可落地的效率规范,以帮助工程师交付出合格的产品。

笔者想对软件的可持续发展尽一些绵薄之力。每一次因为软件的效率问题而浪费的CPU时间和内存不仅浪费了公司的大量金钱,也浪费了能源和硬件,从而对环境产生了严重的影响。因此,在省钱并保护环境的同时为你的企业创造更大的价值,是一件非常有意义的事。

笔者认为写一本书是实现这一目标的最佳方式。这会比通过日常工作、开源项目和会议传播得更广。希望更多的人能够看到本书。

创作背景

在撰写本书时,笔者已经从大量的实践和错误中积累了经验,在软件效率和开发高质量软件方面小有所成。

笔者在19岁时就开始了全职专业软件开发的职业生涯,曾在英特尔从事软件定义基础设施(SDI)工作,同时还攻读了全日制计算机科学专业。笔者的技术栈最初是使用Python为OpenStack项目(https://www.openstack.org)编写代码,然后使用C++在Mesosphere(https://oreil.ly/yUHzn)和Twitter(现X)与一些出色工程师合作编写代码[包括对当时流行的Mesos(https://mesos.apache.org)项目的贡献]。最近几年,转向为Kubernetes(https://kubernetes.io)开发Go程序,并爱上了这门语言。

笔者在英特尔花了不少时间来研究节点超额订阅功能(https://oreil.ly/uPnb7)。通常来讲,超额订阅允许在单台机器上运行比平时更多的程序。这样做是可行的,因为从统计学上来看,所有程序几乎很少会同时使用所有的保留资源。但从笔者目前的认知来看,通过优化软件来省钱通常比使用这样的复杂算法更容易、更有效。

2016年,笔者搬到伦敦,为一家初创游戏公司工作,与谷歌、亚马逊、微软和Facebook的一些前员工合作开发和运营一个全球游戏平台。我们用Go开发了许多微服务,主要是在全球数十个Kubernetes集群上运行。正是在这里,笔者学到了很多有关分布式系统、站点可靠性工程和监控的知识。也许正是在那时,笔者习惯了使用一些出色的可观测性工具,这对于软件效率的落地至关重要。本书第6章中会对此进行说明。

从此以后,一切顺理成章。笔者将对可观测性方面的热情转化为了开发和使用一个名为Prometheus(https://prometheus.io)的流行开源时间序列数据库,以用于监控目的。最终,笔者有幸成为一名官方维护者,同时启动了多个Go开源项目和库。最后,笔者也借此机会与Fabian Reinartz在开源社区中共同创建了一个名为Thanos(https://thanos.io)的大型分布式时间序列数据库。这两个项目目前的使用范围已经非常之广,它们很有可能正在你的公司的基础设施中运行!

2019年,笔者加入了Red Hat,全职从事可观测性系统的开源工作。也因此有了更多深入研究持续性能剖析(continuous profiling)解决方案的机会,本书正文中也会提到这一点。

此外,笔者还担任了云原生计算基金会(Cloud Native Computing Foundation,CNCF,网址为https://cncf.io)的大使和可观测性技术咨询小组(TAG,网址为https://oreil.ly/f9UYG)的技术负责人,与其他成员共同组织会议。通过Prometheus和Thanos项目,笔者的团队每年都会通过CNCF指导计划(CNCF Mentoring Initiatives,网址为https://oreil.ly/rU0bg[1]指导多名工程师。

笔者已经编写或审查了成千上万行生产级的代码,使其具备可靠性和扩展性。到目前为止,笔者累计指导了20多位工程师。然而,最令笔者印象深刻的是主持开源工作,因为在这项工作中,笔者有机会与世界各地不同公司、具有不同背景、目标和需求的人士互动。

总的来说,笔者和共事的优秀人员取得了一些惊人的成就。在一个高质量代码环境中工作是幸运的,因为这样就不会花费时间去优化一些延迟或风格问题。我们致力于良好的系统设计、代码可维护性和可读性,并试图将这些价值观带入开源社区。但是,如果笔者有机会再次开发像Thanos这样的项目,一定会改善一件事:尝试更多地关注代码质量和所选择的算法的效率。笔者会从一开始就专注于制定更清晰的效率规范,并在基准测试和性能剖析上投入更多的精力。

不要误会,笔者这样说并不表示Thanos的性能/效率差,相反,目前Thanos比一些竞争对手更快,而且使用的资源也更少。我们在这上面花了不少时间,但仍然有优化空间,有许多瓶颈等待社区关注与反馈。如果从一开始就应用本书所介绍的知识、技术和建议,笔者相信我们可以将开发成本降低一半,甚至更多,让Thanos达到今天的状态。

这些经历让笔者顿悟写一本这样的书是多么有必要!随着全民编程时代的到来,那些没有计算机科学背景的开发者可能会遇到很多错误和误解,尤其是在软件效率方面。市面上没有太多文献可以为效率或扩展问题提供参考,尤其是对于Go这样的“年轻”语言而言更是如此。希望本书的问世能提供一些指导和帮助。

本书受众

本书旨在提供必要的工具和知识,以便根据具体情况和组织目标来明确何时以及如何应用效率优化。因此,本书的主要受众是使用Go语言和任何其他现代编程语言编写程序的软件开发者。软件工程师的本职工作应该是确保他们创建的软件能在特定的功能需求和效率要求下工作。所以在开始阅读本书时最好具备一些基本的编程技能。

笔者相信本书对那些操作他人编写的软件的人也有用,例如,DevOps工程师、SRE、系统管理员和平台团队。按照一些优化设计层级(如3.5节中所述),有时对软件优化进行投资是有意义的,而有时可能需要在其他层级去解决问题!此外,为了实现可靠的效率,软件工程师必须对生产环境进行大量基准测试和实验(如第7章中所述),这通常意味着与平台团队的密切协作。第6章中介绍的可观测性是目前软件工程中最为推荐的先进工具。笔者强烈建议应该避免为SRE区分应用程序性能监控(APM)和可观测性。如果你在哪里看到了这种区分,极有可能是某些云服务商又要忽悠你付费。正如本书中将要介绍的,可以在所有软件可观测性中复用相同的工具、插桩和信号[2],因为它们同宗同源,有着相同的目标——构建更好的软件产品!

最后,笔者向那些希望保持技术能力,并想了解如何确保团队不会在效率问题上浪费数百万美元的经理、产品经理和领导者隆重推荐本书!

内容组织

本书共分为11章。在第1章中,将讨论软件效率及其重要性;在第2章中,将简要介绍Go及其在效率方面的考量;在第3章中,将讨论优化以及如何思考和处理优化。提高效率可能需要花费大量时间,但系统性的方法可以帮助你节省大量时间和精力。

在第4章和第5章中,将解释延迟、CPU和内存资源,以及操作系统和Go如何将它们抽象化。

从第6章开始,将继续讨论围绕软件效率进行数据驱动决策的含义;在第7章中,将讨论复杂度分析和实验的可靠性;在第8章和第9章中,将解释基准测试和性能剖析技术。

最后两章也很重要。在第10章中,将展示不同优化情况的各种示例;在第11章中,将介绍一些经验教训,并总结在Go社区中看到的各种有关效率的模式和技巧。

排版约定

本书中使用如下排版约定:

斜体(Italic

用于表示URL、电子邮件地址、文件名和文件扩展名。

等宽字体(Constant width)

用于程序清单,以及段落中的程序元素,例如,变量名或函数名、数据库、数据类型、环境变量、语句和关键字。

等宽粗体(Constant width bold

用于表示由用户直接输入的命令或其他文本。

等宽斜体(Constant width Italic

用于表示应由用户提供的值或根据上下文确定的值替换的文本。

该图表示提示或建议。

该图表示一般性说明。

该图表示警告或注意。

示例代码

本书包含一些示例代码,它们将帮助你理解工具、技术和更好地进行实践。所有示例代码都使用Go语言编写,并且适用于Go 1.18及更高版本。可以从https://github.com/efficientgo/examples下载补充材料(示例代码、练习等)。欢迎读者随时通过GitHub Issue或https://github.com/efficientgo/examples/issues提出问题或改进建议!

请注意,为了控制篇幅并获得清晰的视图,本书中的示例代码已经过简化,基本遵循以下规则:

• 如果没有指定Go程序包,则视为main。

• 如果没有指定示例的文件名或扩展名,则假定该文件具有.go扩展名。如果是功能测试或微基准测试,则文件名必须以_test.go结尾。

• 如果未提供import语句,则假定导入了标准库或先前引入的程序包。

• 有时,不会在import语句中提供导入,而是在注释中提供(//import<URL>)。这表示笔者想解释示例代码需要的众多import中的特殊导入。

• 带有三个点的注释(//...)表示删除了一些不相关的内容。这忽略了一些逻辑,以突出更有意义的代码。

• 带有处理错误语句的注释(//handle error)表示已删除错误,以提高可读性。始终在代码中处理错误!

这里的代码是为了帮助你更好地理解本书的内容。通常,可以在程序或文档中使用本书中的代码,而不需要联系O'Reilly获得许可,除非需要大段地复制代码。例如,使用本书中所提供的几个代码片段来编写一个程序不需要得到我们的许可,但销售或发布O'Reilly的示例代码则需要获得许可。引用本书的示例代码来回答问题也不需要许可,将本书中的很大一部分示例代码放到自己的产品文档中则需要获得许可。

非常欢迎读者使用本书中的代码,希望(但不强制)注明出处。注明出处时包含书名、作者、出版社和ISBN,例如:

Efficient Go,作者Bartłomiej Płotka,由O'Reilly出版,书号978-1-098-10571-6。

如果读者觉得对示例代码的使用超出了上面所给出的许可范围,欢迎通过permissions@oreilly.com联系我们。

致谢

俗话说,“三人行,必有我师焉”(https://oreil.ly/owETM),许多人在本书写作过程中直接或间接地帮助了笔者,他们都是笔者的老师,笔者从他们身上学到了很多。

首先,要感谢笔者的妻子Kasia——没有她的支持,这一切都不可能实现。

感谢本书的主要技术审校者Michael Bang和Saswata Mukherjee,他们仔细地检查了所有内容。感谢其他审阅早期内容并提供宝贵反馈的人,他们是:Matej Gera、Felix Geisendörfer、Giedrius Statkevicˇius、Björn Rabenstein、Lili Cosic、Johan Brandhorst-Satzkorn、Michael Hausenblas、Juraj Michalek、Kemal Akkoyun、Rick Rackow、Goutham Veeramachaneni等。

另外,感谢开源社区中众多才华横溢的人,他们在公开内容中分享了大量的知识!他们可能没有意识到自己间接帮助了这项工作。读者将在本书中看到其中一些人的分享,他们是:Chandler Carruth、Brendan Gregg、Damian Gryski、Frederic Branczyk、Felix Geisendörfer、Dave Cheney、Bartosz Adamczewski、Dominik Honnef、William(Bill)Kennedy、Bryan Boreham、Halvar Flake、Cindy Sridharan、Tom Wilkie、Martin Kleppmann、Rob Pike、Russ Cox、Scott Mayers等。

最后,感谢O'Reilly出版社的编辑团队,尤其是Melissa Potter、Zan McQuade和Clare Jensen提供的出色帮助。

欢迎反馈

如果你对笔者的工作有兴趣,或者想在这个领域了解更多,请在X(Twitter)上关注笔者(https://twitter.com/bwplotka)或访问笔者的博客(https://www.bwplotka.dev)。

O'Reilly在线学习平台(O'Reilly Online Learning)

40多年来,O'Reilly Media致力于提供技术和商业培训、知识和卓越见解,来帮助众多公司取得成功。

我们拥有独一无二的专家和革新者组成的庞大网络,他们通过图书、文章、会议和我们的在线学习平台分享他们的知识和经验。O'Reilly的在线学习平台允许你按需访问现场培训课程、深入的学习路径、交互式编程环境,以及O'Reilly和200多家其他出版商提供的大量文本和视频资源。有关的更多信息,请访问https://oreilly.com

如何联系我们

对于本书,如果有任何意见或疑问,请按照以下地址联系本书出版商。

美国:

O'Reilly Media,Inc.

1005 Gravenstein Highway North

Sebastopol,CA 95472

中国:

北京市西城区西直门南大街2号成铭大厦C座807室(100035)

奥莱利技术咨询(北京)有限公司

要询问技术问题或对本书提出建议,请发送电子邮件至errata@oreilly.com.cn

本书配套网站https://oreil.ly/efficient-go上列出了勘误表、示例以及其他信息。

关于书籍和课程的新闻和信息,请访问我们的网站https://oreilly.com

我们在LinkedIn上的地址:https://linkedin.com/company/oreilly-media

我们在X(Twitter)上的地址:https://twitter.com/oreillymedia

我们在YouTube上的地址:https://youtube.com/oreillymedia


[1]如果你是软件开发或开源方面的新手,可以与我们联系,或开始尝试提交贡献,并申请为期两个月的带薪指导。如果你想在指导他人的同时获得乐趣,请告诉我们!我们也需要优秀的导师——指导新一代开源维护者非常重要。

[2]笔者已经收到了一些经验丰富的人的反馈,他们甚至不知道可以借助指标(metric)来提高效率和性能!这是基本操作,通过本书可以学到怎么做。