Go at Cloudflare

文摘   2024-09-24 12:48   上海  

本篇内容是根据2019年12月份Go at Cloudflare[1]音频录制内容的整理与翻译,

Jaana[2]Jon[3]Mat[4] 与 Cloudflare 首席技术官 John Graham-Cumming[5] 一起讨论了 Cloudflare 的 Go 语言以及 John 在 戈登·布朗(2007~2010期间的英国首相) 向 艾伦·图灵 道歉事件中的独特参与。Cloudflare 是如何开始使用 Go 语言的?他们使用 Go 解决哪些问题以及何时转向其他语言?John 向图灵道歉的请愿书究竟为何如此受欢迎?

过程中为符合中文惯用表达有适当删改, 版权归原作者所有.



Mat Ryer: 大家好,欢迎收听 Go Time。我是 Mat Ryer。今天我们将讨论 Cloudflare,这可以说是 Go 的一个真实成功案例。

今天我们邀请了几位常驻嘉宾和一位特别嘉宾。Jon Calhoun 在这里。你好,Jon。

Jon Calhoun: 你好,Mat。

Mat Ryer: 你最近怎么样?

Jon Calhoun: 我很好。

Mat Ryer: 很好!我们还邀请了 Jaana B. Dogan。你好,Jaana。

Jaana Dogan: 你好!

Mat Ryer: 最近怎么样?

Jaana Dogan: 很好!

Mat Ryer: 我们今天的特别嘉宾是 Cloudflare 的首席技术官 John Graham-Cumming。欢迎你,John!

John Graham-Cumming: 你好!很高兴能来这里。

Mat Ryer: 感谢你加入我们的节目,我们非常兴奋。节目一开始,你能为大家简单介绍一下你自己和 Cloudflare 吗?让那些不太了解的人有个背景。

John Graham-Cumming: 当然。如果你不知道 Cloudflare 是什么,我总是对人们说,你今天可能已经用过它了,只是你没意识到。我们是一家基础设施公司,保护、加速并提高了大约 2000 万个域名的可靠性。这包括从你可能使用的网站,到你手机上某个应用程序的 API 后端。我们检查这些东西,所以,如果你访问了某个网站或使用了某个应用,而它使用了我们,我们会检查你是否是黑客,判断你不是的话,就让你通过。或者,我们会让连接更快、更可靠。

所以我们是互联网基础设施的一部分,虽然隐形,但确实存在。我是 Cloudflare 的 CTO,我在 Cloudflare 工作了八年。最初我是程序员,写了很多 Go 代码;实际上,我在 Cloudflare 写的第一行代码就是 Go 代码……现在我在这里,准备和大家聊一聊这些。

Mat Ryer: 很棒。我记得你在第一届 GopherCon 上做过演讲,对吗?是关于 channel 的演讲。

John Graham-Cumming: 没错,或者是第二届?是其中一届早期的会议。我做了一个关于 channel 的演讲,讲的是为什么我非常喜欢它们。这可以追溯到我读博士时候的事情,因为我做了很多博士研究---那时远在 Go 出现之前,但我使用了类似的编程范式。

Mat Ryer: 很酷。所以当你看到 Go 语言中有 channel 时,是不是立刻被吸引了?

John Graham-Cumming: 是的,这对我来说是个巨大的吸引点。我们要追溯到 80 年代末、90 年代初,我当时用的是一种叫做 CSP(通信顺序进程)和相关语言 Occam(译者注: occam是一种并发编程语言,建立在通信顺序进程(CSP) 进程代数的基础上,并且具有许多相同的功能。它以哲学家奥卡姆命名)。这两者都有 channel 和通过 channel 进行同步通信的概念。当 Go 出现时,它的介绍中提到“我们受到了 CSP 的启发”,这对我来说是一个启示,因为我想“我很久以前就做过这些了。” 我做了大量的其他编程,当时我想“终于有这种编程方式了。”

我非常喜欢这种思考和推理程序运行方式的方式,而现在它出现在一种类似 C 的语言中,因为我做过很多 C 和 C++ 编程。而且它有垃圾回收功能……这是一种有趣的组合,这就是我从一开始就对 Go 感兴趣的原因。

Mat Ryer: 很有趣。CSP 和 Go 中的 channel 设计相同吗?还是有区别?

John Graham-Cumming: 这取决于情况。如果你追溯到 CSP 的最初时期,实际上没有明确的 channel,虽然很快之后就有了 channel 的概念。CSP 的真正有趣之处在于,它的全称是通信顺序进程(Communicating Sequential Processes),所以你有一组进程按顺序执行,即 goroutines 它们各自做自己的事情……然后通过同步来进行通信。这是关键所在。如果你想想 Go 中的无缓冲 channel,就会有一个明确的同步时刻:当这个消息被发送时,另一个 goroutine 会接收它。

这种明确的通信同步概念使得推理程序的行为变得相对容易。虽然它不能消除所有问题,但它比完全异步的东西要容易理解得多……这让我非常喜欢,而这也是 CSP 的核心思想。

Jaana Dogan: John,你有没有看到过 Bell Labs 的一篇旧论文?也许是 Plan 9 时代的……他们在概念上解释了一些关于通过 channel 进行同步的概念。我认为那篇论文中的一些概念正好在 Go 中得到了很好的实现……如果我能找到那篇论文,我会发给你。看到这些概念的相似性真的很有趣。

John Graham-Cumming: 是的,Plan 9 很有趣……Plan 9 出现在 80 年代,而 CSP 的基础是 1977 年和 1978 年的。所以很多这些东西的起源可以追溯到对同步的思考……还有人们在思考“如果我们有非常大的多处理器机器怎么办?”,尽管当时没有,但他们在思考如何驾驭这些机器,而这种思维方式是一种驾驭它们的方法。

Mat Ryer: 很有趣……理论的出现比实际的应用要早一些。

John Graham-Cumming: 是的……我认为这在很多理论中都是如此。如果你回顾许多计算机科学的历史,尤其是 70 年代,有很多讨论分布式系统问题、时钟同步、我们该如何思考这些问题、如何推理等等的论文……因为人们已经可以预见到将会有多处理器机器的出现。虽然当时并不普遍,但人们已经可以看到“哦,这将会发生。”

1978 年的 Communicating Sequential Processes 论文[6]中甚至有一句话:“然而,处理器技术的发展表明,由多个类似的自包含进程构建的多处理器机器可能会比伪装成单处理器的机器更强大、容纳更多、更可靠和更经济……”,你可以看到,“哦,这将会发生”,那是在 1978 年……然后它真的发生了。

Mat Ryer: 太棒了。那 Cloudflare 现在有多少 Go 代码?从一开始就有 Go 吗?还是之前使用了其他语言?

John Graham-Cumming: 我并不是一开始就加入 Cloudflare 的。我加入时,公司只有 24 个人,Cloudflare 已经在运行了。他们已经有一个服务为几千个域名提供服务,而如今这个数字是 2000 万……但它已经在运行,当时是一个非常混合的技术栈。

Cloudflare 核心的实际请求处理部分---今天每秒处理 1000 万到 1500 万个请求---当时是用 PHP 写的。还有一些 C 代码,因为我们修改了 NGINX 的 C 模块,使其在某些方面运行得更快……还有一些 C++ 用于日志文件处理……所以这是一个多语言的混合体。公司当时刚刚开始使用 Lua。

当时的计划是用 Lua 重写 PHP,因为 NGINX 和 Lua 有很好的集成,OpenResty 允许你使用 Lua 和 NGINX 编写 RESTful 程序。当我加入时,这个从 PHP 迁移到 Lua 的过程正在进行中……但还没有 Go,因为我们说的是 2011 年,是 Go 诞生两年后的版本,还没有到 1.0。

我加入时,我们在使用 0.98 版,还是第一个正式发布版本之前。我之前已经看过 Go,并且对它很感兴趣……但实际上,我加入 Cloudflare 后仅仅两周外加一天,我就写了几千行 Go 代码,展示我们如何在实际产品中使用它。那是一个现在依然存在的产品。

Mat Ryer: 这很有趣……所以你实际上解决了一个真实问题并在内部展示了它。

John Graham-Cumming: 是的,因为我非常确信它适合我们要解决的问题--- 我们有很多计算机,分布在世界各地,拥有大量 CPU,而且我们的工作很多是 I/O 密集型的……因为我们基本上处理的是网络相关的事情,而我认为 Go 的 net 库非常棒。两周后,我已经有了一个运行中的原型。

Mat Ryer: 你的原型中使用了 channel 吗?

John Graham-Cumming: 当然。我们有一个叫做 Railgun 的东西(译者注: Railgun是一个用来加速动态内容传输的产品,2024年年初已经被Cloudfalre弃用Cloudflare is deprecating Railgun[7])。Cloudflare 的工作之一是我们位于最终用户和 Web 服务器之间……这可能会引入额外的延迟,因为用户不是直接访问 Web 服务器,而是先访问我们。如今,由于我们网络的规模,这实际上不是问题,因为我们的网络非常庞大……但当时我们非常关心如何加快我们与 Web 服务器之间的连接。

所以 Railgun 的想法是,如果我们接管两者之间的连接并且不使用 HTTP,我们可以做很多有趣的事情,比如压缩;尤其是可以做增量压缩,意识到网站的变化速度并不快,如果你多次请求同一页面,它可能只改变了几个字节。因此你可以得到非常高的压缩率,可能比原始大小小 99.5%。

但我们是一个共享环境,所以你会有大量的 Railgun 对后端成千上万的 Web 服务器进行通信,拥有数千个连接。所以如果我们为每个连接使用一个 goroutine,外加一些协调的 goroutines,再加上到处都是 channel……

Mat Ryer: 你的演讲还涵盖了一些关于在 Go 中使用 channel 的实际操作和技巧,我至今仍会参考那些模式……

John Graham-Cumming: 是的,那真的挺有趣的,因为很多内容其实就是我在大学时做的事情,我当时会想“好吧,这些就是你可以做的事情。你可以实现特定的同步,或者你可以用不同的方式协调事情”,那次讲座的目的就是向大家展示通道(channel)的各种可能性……它们不仅仅是从程序中获取输出的机制,它们是基础。这就是我那次演讲的想法,只是为了给大家一些启发。我记得那好像叫《通道百科全书》(Channel Compendium)(译者注: GopherCon 2014 A Channel Compendium by John Graham-Cumming[8])……是这个名字吗?我现在不太记得了,已经有一段时间了。

Jaana Dogan: 你最后有没有重构其中的一些部分?我看到很多人开始重新审视以前的一些并发模式,比如他们随着时间推移总结出来的模式……那么在 Cloudflare 也有类似的情况吗?

John Graham-Cumming: 当然有,尤其是在 Railgun 上……因为当时我在做 Railgun 时,一边学习这门语言,一边开发我们的一款产品……所以肯定有很多时候我做的事情其实可以做得更好,后来进行了大量重构……当然,最终一个团队接手了它,按照经典的风格,他们会说“哪个笨蛋写的这东西?我们需要重构一切……” [笑声]

Mat Ryer: “把他的权限收回……”

John Graham-Cumming: 是的,完全正确。 [笑声] 没错。所以,当然有重构……但还有一个非常重要的事情要意识到,那时我们用的是 0.98 版本的 Go,所以有一些 Bug,还有一些功能没有实现。最著名的例子是---我们运行在 Linux 上,因为 Cloudflare 到处使用 Debian……但我们在发布软件时---Railgun 有一个组件是在客户的网站上运行的,而我们的一位客户使用的是 FreeBSD;Go 是支持 FreeBSD 的,但不幸的是,它会很快耗尽内存……这就是那种你会想“为什么它在 Linux 上不会耗尽内存,而在 FreeBSD 上会?”的情况。于是我得到了一个学习垃圾回收器的机会,了解了它的工作原理。这就是开源软件的美妙之处---我可以自己调试。我去读了所有关于垃圾回收器的代码……

最终,我意识到一切看起来都正常。问题出在内存被收回到操作系统时---垃圾已经被回收了,然后你决定“我真的不再需要这部分堆内存了,我可以把这部分内存还给操作系统”,这触发了 Go 运行时中的一个叫 sysUnused() 的函数。而 sysUnused() 应该调用操作系统中的 madvise(),告诉它“我不再需要这块内存了”。它在 Linux 上会这样做,但在 FreeBSD 上没有。而原因就是,我显然查阅了源代码,发现了:

*// TODO (rsc)*

这就是为什么我们耗尽了内存,因为每次我们把内存还给操作系统时,我们说“我们不需要它了”,但操作系统却没有真正回收内存……

Jaana Dogan: 这太不可思议了……

John Graham-Cumming: 所以这是一个相对容易的修复。我进去修复了它……如果你倒回去看,会看到我提交的请求说“顺便说一下,我正在为 FreeBSD 实现这个功能”,因为它之前是缺失的。所以在早期确实有很多这样的问题。

另一个问题是---syslog 包需要一些改动;它与 RFC 标准并不完全兼容,所以我也修复了它,因为 syslog 对我们非常重要……还有一个当时非常困难的问题是 sync.Pool 不存在。

实际上,我在《通道百科全书》中谈到了一些事情,比如“在没有 sync.Pool 的情况下,如何在 Go 中回收内存?”你可以通过通道实现这个需求,并且有一个非常漂亮的模式。因为从根本上讲,如果你考虑 Railgun 内部发生的事情,有很多是“我们正在进行 HTTP 请求,把它发送到这里”,然后你又要释放它。但接着你又需要另一个 HTTP 请求。如果你反复在堆上分配和回收内存,你最终会得到一个混乱的情况和大量的垃圾……所以我不得不解决这个问题,这也是一个大的调试过程。

Mat Ryer: 那当你遇到这些问题时,有没有让你感到沮丧?这些是否给你敲响了警钟,让你觉得“也许这不是个好选择”?我不知道如何更好地表达这个问题……

John Graham-Cumming: 嗯,我觉得我很幸运,那时 Cloudflare 还在发展中;它还是一个小公司。我并不是进入一个已经成型的大公司……所以即使遇到这些问题,我其实可以尝试很多东西。我们当时发展得非常迅速,并且实现了很多功能。我认为在早期很多事情都很困难。

但说实话,我过去 25 年里用过的每一种编程语言都有遇到问题,甚至可能是 30 年。我认为你总是会遇到操作系统、库等带来的问题……我做了很多 C++ 编程,不要让我开始讲 C++ 的库有多么可怕……

Mat Ryer: [笑声] 是的。而现在大家可以更加自信地使用 Go,这要归功于像你这样的努力,人们修复了那些不适合他们的功能。

Jaana Dogan: 我认为 Cloudflare 在解决一些关键问题上起到了非常重要的作用……我记得那个闰秒 Bug,你写了一篇事后分析。当时没有单调时钟(monotonic clock),大家讨论了很多,但没有人真正关心是否可以把它作为一个额外的 API 加入标准库。那个情况变得非常关键,我记得他们好像在下一个版本中发布了一些东西,对吗?

John Graham-Cumming: 是的,他们发布了,我很高兴他们这么做了,尽管我始终相信“工欲善其事,必先利其器”这个格言……所以我们本不应该出现那个 Bug。

Jaana Dogan: 是的。

John Graham-Cumming: 我们假设时间 API 是单调的,但事实并非如此……这确实很让人害怕。不过,显然通过我们犯的这些错误,事情得到了进展。

对我们来说最大的挑战是,在 Go 1.5 之前,垃圾回收暂停确实是个问题,尤其是当堆变得很大时,这引发了很多问题……特别是在我们运行的世界上最大的权威 DNS 服务上(译者注: 1.1.1.1),它完全由 Go 编写。是我们内部的 DNS 服务器。如果你曾访问过 Cloudflare 的网站,或者使用过我们的 API,那么你就已经向其中一个服务器发起过 DNS 请求……有趣的是,当垃圾回收暂停时,我们可以看到 DNS 服务器的性能下降,外部的测量服务也可以感知到……而 Go 1.5 真的解决了这个问题。这是一个巨大的改进。

除此之外,我们在优化方面也投入了大量精力,特别是在加密相关的优化上。我们贡献了很多汇编级别的优化,让 Go 的网络和加密库变得非常高效。

Mat Ryer: 这太棒了。有遇到过 Go 不太适合的场景吗?比如你为某个问题尝试用 Go 解决,但发现太费劲,最后转而使用了其他语言?

John Graham-Cumming:  我们并不是所有事情都用 Go……我认为对于 I/O 密集型任务,或者你需要大量并行和并发的时候,Go 非常合适。如果是做底层的位操作之类的事情……例如,Cloudflare 做很多图像处理工作---调整大小、重新采样等各种操作。我认为有些语言更适合在底层进行细致的操作。

我知道在早期,确实有些时候我们觉得好像在与语言作斗争,比如在垃圾回收问题上。但现在我不再有这种感觉了,现在的问题是“我们为解决这个特定问题,该选择哪种语言?”我认为 Go 在很多方面都很出色,我们也广泛使用它。

Jaana Dogan: 你们有没有选择语言的指导原则?我看到一些大公司会这么做……“如果你在解决这种类型的问题,最好不要使用这种语言。”他们至少会记录一些反模式。

John Graham-Cumming: 我们没有正式的指导原则。我们内部会根据项目进行讨论,然后大家可以相对自由地选择他们想在 Cloudflare 使用的语言。我们实际上使用的语言种类并不多,但通常工程师会根据他们的需求做出选择,通常是在少数几种语言之中选择。

Jon Calhoun: 当你们早期采用 Go 时,我知道很多人会觉得使用一门新语言很难组建团队,因为你不能说“我要一个有两年经验的 Go 开发者”,因为这门语言才刚出来两年。这是不可能的。有人在 Twitter 上问了 Mat 这个问题,对吧,Mat?

Mat Ryer: 是的。Dylan M. 在 Twitter 上问:“开发者是通过工作中学习 Go,还是你们现在只招已经会 Go 的人?这些年来有变化吗?”

John Graham-Cumming: 我们绝对不会只招已经会 Go 或 Rust 的人。我认为那是一个巨大的错误,如果你只招有这些特定技能的人,你会排除掉很多优秀的人。而且我认为程序员通常对学习新东西和掌握新技能充满热情。我们非常欢迎大家在工作中学习 Go。

现在有很多学习 Go 的资源,它是一门相对容易上手的语言,而且我们还有很多其他 Go 程序员……我们也有很多其他的 Rust 程序员。所以我认为如果我们规定“你必须会 Go”,那是一个错误。当然,如果你会,那很好,但从根本上来说,技术变化非常快,我认为编程---尤其是在像 Cloudflare 这样快速变化的环境中---是一份需要不断学习的工作。你得不断学习新东西,语言是其中的一部分,库也是其中的一部分。你必须勇于学习。

如果有人来到我们这里说“我只愿意用 Go 编程,这是我一生的热爱,我只写 Go”,我可能会有点担心,因为这意味着他们可能会陷入一种固定的思维模式,不愿意尝试其他东西。

Mat Ryer: 这很有趣……因为这种态度完全与很多招聘的做法背道而驰。我看到有人在 Twitter 上说:“Go 已经十岁了,所以我终于可以申请那些要求有十年 Go 经验的工作了……” [笑声]

John Graham-Cumming: 是的,但你知道,那些要求十年 Go 经验的人真的很缺乏想象力。这可能是个信号,告诉你不应该去那些地方工作。

Mat Ryer: 是的。

Jon Calhoun: 我觉得这也是一个信号,说明写这个招聘要求的人可能并不怎么用 Go,因为---虽然人们在早期确实会犯一些错误,最终学会采用或避免某些模式……但在我看来,Go 的一个优点是,你不需要十年的经验就能高效使用它。

Mat Ryer: 对。

John Graham-Cumming: 如果你真的需要十年经验才能在一门语言上高效工作---嗯,我觉得应该考虑淘汰这门语言了。 [笑声]

Mat Ryer: 没错。

John Graham-Cumming: 话虽如此...

Jaana Dogan: 其实有太多语言可以列举在这个范畴里了... [笑声]

John Graham-Cumming: 我本来想说,我不确定是否真的有人能完全掌握 C++。我觉得每个写 C++ 的人都写出了自己版本的 C++,这大概也是人们喜欢它的原因之一吧... 但我认为,根本上来说,程序员喜欢学习;他们想要做新的事情。早期一旦 Go 开始流行,我们开始推广它,人们想为我们工作就是为了用 Go。所以他们发现了它,或者他们对它很感兴趣,他们来找我们工作---你希望从程序员那里得到的是动力。你希望人们有动力。如果你有内在的动力,比如“嘿,我在学习,我在成长”,这就太棒了。作为一家公司,你为什么不雇佣那些想要这样做的人呢?

Jon Calhoun: 那么在这个过程中你遇到过问题吗?很多人一开始学 Go 时会认为“Go 有这些很棒的通道(channel),我想在很多地方用它们”,但实际上有些地方不适合用,或者有些时候会显得过于复杂。你有需要对此进行调整或劝阻吗?你是怎么处理这些情况的?

John Graham-Cumming: 确实有时会发生这种情况... 虽然我觉得这类问题会自我修正,因为人们会构建出一些非常复杂的东西,我自己肯定也这样做过。在 Railgun 的早期版本中,我可能有一些奇怪的 goroutines,它们从一个通道读取数据,再写入另一个通道,就像数据库中的外键表一样... 但我认为这种问题会自己解决,因为人们会意识到他们构建的东西太复杂了。我们内部也会进行代码审查,所以人们会指出“这种做法很奇怪”。

Mat Ryer: 是的,我也绝对犯过这种错误。当我第一次看到通道时,我就想“这简直太棒了。你想让我连接一些字符串吗?没问题,我会用通道来做。” [笑声] 是的,绝对没问题。而有了更多经验后,有时你会想“你知道吗?用锁(mutex)可能会更简单。我就用锁了。”

John Graham-Cumming: 是的,我的意思是... 这的确是一个例子。有时这种方式很好。如果你在同时访问一个 map,那么用锁来包装它是可以的,可能不需要让一个 goroutine 通过通道处理 map... 我猜你也可以这么做,但最终很多问题归结为优化。如果你构建了一个不是最优的东西,也没什么大不了的,因为如果它在你的环境中能正常工作,那就足够了... 之后你可以去衡量它,看看哪里不好。 (译者注: 过早优化是万恶之源)

Mat Ryer: 我认为从 Cloudflare 的首席技术官口中听到这话非常重要... 因为太多程序员过早地痴迷于完美化,会说“我们不在乎它是否易于阅读和维护,我们只关心榨取所有的性能...” 而显然,如果你过早这么做---这是我们所有人都会犯的一个错误---你会做出错误的假设。像 Cloudflare 这样规模的公司---听到这样的观点真的颇具鼓舞性。

John Graham-Cumming:  我一开始是做 C 和汇编的,写网络设备驱动程序,那时每一个 CPU 周期都很重要。你不会用 LDA 0,而是用 XOR AA,因为那只占一个字节,而且更少的 CPU 周期是关键。但那种情况对特定的环境是合适的。现在我们有多核处理器... 显而易见的是你应该测量性能,因为测量性能的一个好处是你经常会对问题感到惊讶。你会想“为什么会发生这种情况?”而如果你只是凭直觉,通常是完全错误的,尤其是在大型系统中;你不一定知道问题出在哪里。

我知道程序员喜欢表现得很聪明,优化代码的诱惑很大。“我要写一个更快的 strcmp 函数。”不,其实你做不到。

举个例子,我们在 Go 中优化了加密部分,因为我们处理了大量的 HTTPS 请求,所以优化加密是很有必要的。我们有一个曾在 Intel 工作过的工程师,他热爱做这类工作... 所以我认为你应该测量并找出问题所在,而不是从一开始就担心这些事情,因为你最终可能会优化错误的部分。

Jaana Dogan: 说到测量... 你们在哪里进行测量?你们测量的是生产环境的性能吗?数据来源是哪里?

John Graham-Cumming: 是的,我们可以测量生产环境的性能。有很多很好的工具可以做到这一点。你可以使用像 strace 这样的工具来弄清楚你的程序在生产环境中做了什么。我们在需要完全理解某件事时会在生产环境中使用它。

当然,我们也有测试环境,但我必须说,当你像 Cloudflare 这样规模运营时,令人惊讶的是流量的多样性。访问 2000 万个网站的流量与你在测试环境中预期的流量完全不同;很难复制... 所以现实世界会给你带来惊喜,因此在生产环境中进行测量是有帮助的。

我们经常使用的一种工具叫做火焰图(flame graph),它可以让我们深入了解哪个函数、哪部分代码花费了大量时间... 当我们需要进行优化时,我们有工具来帮助我们做这些工作。

Mat Ryer: 在你们的情况下,代码优化和其他我们通常认为只是技术上的练习,在你们这里有时可能会带来显著的商业影响。如果你只有少量用户,当然,你几乎不需要考虑这些问题。但在你们的规模下,这些就很重要了... 有没有遇到过在业务需求和技术需求之间的拉锯战?

John Graham-Cumming: 实际上没有,至少从优化的角度来说没有... 因为我们有一个财务模型,能够告诉我们节省 CPU 时间能为我们节省多少钱... 我们增长得非常快,如果我们可以不用那么快购买新硬件,那就能为我们节省大量资金... 所以优化可以直接转化为金钱上的节省。

现在有趣的是,我们优化的目标不只一个。一个是 CPU 利用率,因为我们想买更少的机器。另一个是延迟,因为我们希望用户使用我们服务时,访问网站的速度最快。在某种程度上,这两者是相同的,但不总是如此;这取决于边缘机上的实际工作负载。有些事情不涉及延迟的关键路径,但可能占用了大量的 CPU。

所以唯一的真正权衡通常是“是开发这个新功能更好,还是做优化更好?”这可能是我们需要做出的权衡... 因为可能有一些新产品你想要开发并推出,并且会有一些预计的收入,或者你想抢先,或者你想击败竞争对手... 与“同样的工程团队可以为我们节省全球 CPU 利用率的 x%,从而节省数百万美元”的权衡。

这些决定需要做出,我们有一个产品管理团队来帮助我们做出这些关于如何分配工程资源的决定。

一个与 Go 相关的有趣例子是我们有一个内部产品,它类似于负载均衡器。如果你考虑 Cloudflare 过去 8-9 年的发展,我们有多代硬件;我们在 194 个城市都有硬件,这些硬件的性能各不相同... 我们希望确保每台机器在任何城市的 CPU 利用率都相同。如果你采用简单的负载均衡方法,这并不会发生,因为所有机器无法处理相同的负载。

一个非常典型的例子是,一些机器的负载是 50%,而另一些则是 75% 或 80%。所以我们编写了一个协调层,实际上是在测量这些机器的性能,了解它们的能力,然后实时地在机器之间分配流量... 而这个协调过程是用 Go 编写的。这个系统确实让我们达到了理想状态。在 Cloudflare 内部有非常显著的图表,你可以看到 CPU 利用率的分布情况,然后突然你打开了这个叫做 Unimog[9] 的系统,整个图表就平坦了。每台机器的 CPU 利用率完全相同。所以 Go 在很多方面得到了应用。

Mat Ryer: 看到那些图表变化的那一刻一定很满足吧...

John Graham-Cumming: 如果你使用过 Cloudflare,你可能知道,当你在 Cloudflare 上做配置更改时,无论是点击按钮,还是上传代码到我们的边缘计算环境,这些更改都会快速全球同步。这个过程依赖于我们内部的一个叫做 Quicksilver[10] 的分布式键值存储系统。它是用 Go 编写的... 一般来说,这些更改会在半秒内传播到全球各地。从新西兰到亚特兰大,再到智利的圣地亚哥。再一次,这是 Cloudflare 核心的一部分,能够非常快速地进行这些更改... 这又是一个 Go 程序。

Mat Ryer: 这真是太酷了。你能给我们讲讲它是怎么工作的嘛?我是不是读过一篇关于 Quicksilver 的博客?

John Graham-Cumming: 我们提到过几次,但我们还没有详细介绍它。其实我们现在正在准备公开所有关于 Quicksilver 的细节,并且我们想整理代码,以便开源它,让其他人也能使用它。

基本上,我们在全球运行一个日志文件系统;你可以把数据推送进去,然后你可以随时找到自己在日志中的位置并赶上最新的更改。所以如果某台机器需要赶上最新的更改,它只需要通信并说“好吧,我现在在这个检查点,给我提供差异数据。”

Mat Ryer: 这就是为什么你总是为我们找到时间相关的 Bug...

John Graham-Cumming: 也许吧... [笑声] 我想负责这个系统的团队可以告诉你他们发现的所有问题。我们研究过很多其他的分布式键值存储系统,它们往往是为单个数据中心的多台机器设计的。而 Cloudflare 的“数据中心”其实是整个地球。问题在于世界各地的网络丢包率和延迟非常不同。应对这些问题是我们设计这个系统的初衷。

Mat Ryer: 是的,太酷了。我非常想了解更多。虽然我没有实际的使用场景,但这是出于一种极客式的好奇心...

John Graham-Cumming: 是的,完全理解。

Jaana Dogan: 说到这个---你们有很多开源项目,很多人将它们作为参考... 这一切是怎么开始的?是因为你们的文化想要推动开源,还是你们有意去分享,因为 Go 在一开始非常小,而你们是主要的用户之一?

John Graham-Cumming: 我过去也做过开源项目,所以我认为开源很重要。我觉得工程师看到他们的工作被他人使用是非常有成就感的,所以鼓励大家开源并不困难。而且显然,如果你在修改某些东西,那你应该把修改内容提交给上游。上游并不总是会接受你的修改,但你至少应该尝试。

我们有一些开源的规则。首先,开源的东西必须是在生产环境中使用的。这样做的原因是我们不想付钱让人们写他们随意想写的开源项目... 他们最好是写 Cloudflare 需要的东西。所以我们的规则是,只要某个东西在生产环境中使用,我们就可以开源它,也就是说我们真的需要它。

然后是关于维护负担的问题。开源的一个问题是,人们会提交 pull request,你就得投入时间去处理。所以我们倾向于开源那些我们认为其他人也能轻松使用的东西。这通常是小程序、库、独立的技术...

Cloudflare 有一些部分... 比如我们很多人会说“你们能不能开源你们的 DNS 服务器?”但问题是它与我们的业务逻辑紧密集成,因为 DNS 是我们业务的核心之一。所以我们会需要把业务逻辑抽象出来,为开源项目创建一个抽象结构,这会变得非常工作量大,对我们来说没有意义。

Mat Ryer: 是的。关于开源还有一个好处是,先确保开源的东西在生产环境中使用,这样你知道它有用,而且它确实有效。我认为这是对任何人的一个很好的建议---不要只是凭空想象一个包并构建它... 当然,你可以那样做,这是学习和探索的好方法... 但最好的开源包通常是解决了开发者自己的问题。

不过正如你刚才举的例子,John---它们并不总是适合开源,即使你非常愿意这么做。有没有遇到过争论?有时人们在决定是否开源某个东西时会因为公司知识产权而紧张。你们遇到过这种讨论吗?

John Graham-Cumming: 说实话,没有。在 Cloudflare,开源的流程是内部有一个邮件列表。你只需要发邮件说“嘿,我想开源这个东西。我打算使用这个许可证。” 我们有少量被批准使用的许可证... 在那个列表里有我自己、几位其他资深技术人员和我们的一些法律团队成员。老实说,通常回应是---如果我在睡觉,当然不会立即回复,但大多数情况下,我们会在几小时内回复开源请求,通常是同意的。

我们之所以能这么做的部分原因是,Cloudflare 的 CEO Matthew[11] 曾多次说,他不认为 Cloudflare 编写的任何代码能给我们带来长期的竞争优势。所以这意味着开源大多数代码没有什么风险。

我们觉得即使我们把内部的 Git 仓库全都公开,最终也不会对我们造成长期的伤害。当然,我们不会这么做,因为维护这类事情的负担太大。

(译者注: 赛博活菩萨名不虚传...)

Mat Ryer:  它运行在 AWS 上吗? [笑声]

John Graham-Cumming: 天哪,不。绝对没有。

Mat Ryer: 那就没什么意义了。

John Graham-Cumming: 我们绝对不会使用 AWS。

Mat Ryer: 嗯,没错。这真的很有趣……听起来其他公司也可以从 Cloudflare 的例子中学到一些东西。我觉得这种慷慨和社区精神最终只会给公司带来好处,但当我和其他人交流时,很多公司在这方面都有些紧张。你们的博客也是一样的,John。对于那些不知道的人,Cloudflare 的博客是 blog.cloudflare.com[12],它是一个包含各种技术内容的绝佳资源,对吧?

John Graham-Cumming: 是的,我加入 Cloudflare 时公司已经有了博客,Matthew 和其他几个人也在为它写文章。但我真的想写一些非常深入的技术文章,部分原因是我喜欢这样做,而且我认为其他技术宅也喜欢阅读深入的技术内容。所以我和其他几个人开始这样做,现在我是 Cloudflare 博客的主编。

我告诉大家,当他们为博客写文章时,我们的目标是教育读者。所以我会回去告诉他们:“你需要向读者解释你在谈论什么---背景是什么,主题是什么。”因此你会发现我们的一些博客文章非常长,写起来也非常费时。

我们有一个非常出色的插画师,Carrie,她为博客制作插图……我们在这方面花了很大的心思,部分原因是为了吸引人才,因为人们读了这些文章后会想:“我想为 Cloudflare 工作”;部分原因是我们的客户也会通过这些文章了解我们在做什么样的工作以及我们参与的技术深度;还有部分原因是我们觉得这很有趣。其他技术宅也喜欢知道这些东西。

Mat Ryer: 绝对是的,请继续保持下去,我们非常喜欢这些内容。这是事实。

John Graham-Cumming: 是的,我们肯定不会停下来的。我知道---因为我审查博客上的所有内容,我知道可能有一些工程师在看到我评论时会叹气,因为我可能会说:“这还不够好。你需要再写 500 字来解释你在说什么。”

Mat Ryer: 对,没错。

Mat Ryer: 稍微换个话题,每年在 GopherCon UK[13](英国的 Go 语言大会)我们都有一个小传统,就是去参观布莱切利公园[14](译者注: 布莱切利公园是二战时英国政府代码与密码学校的代名词,它在战争中起到了关键的情报破译作用)。John,你和艾伦·图灵有一些关联,对吧?如果可以的话,我很想听你讲讲这个故事。

John Graham-Cumming: 好的,当然可以。我曾经在国外生活了很长时间,现在也是如此。但 2009 年我回到了英国,我想我看到了 Stephen Fry 的一条推文[15],大意是“如果艾伦·图灵没有自杀的话,今天就是他的 90 多岁生日。” 作为一名计算机科学家,我知道图灵的故事,对计算机安全也很感兴趣……所以你最终会经常遇到图灵的故事。

我当然知道他自杀了,也知道他在破译密码和人工智能领域的贡献……当时我在家里,心情非常不好。我真的觉得,这是因为我们从来不谈论图灵,似乎是因为这是一件令人羞耻的事---“他因为是同性恋被起诉,然后我们对他很不好,所以他自杀了。” 这是一种典型的英国风格---我们忘记了谈论他。

我想:“如果我们能一次性把这件事说开了,我们就可以公开谈论它,承认我们做错了。” 然后我们就可以庆祝他所做的贡献。所以我在自己的博客上写了一篇文章,说:“这太糟糕了。英国应该道歉。” 然后有个人在评论中说:“你知道吗,你可以在 唐宁街10号 的网站上发起请愿。” 所以我立刻上网,发起了这个请愿。大约等了一个月才获批,我当时真以为可能会有 500 人签名。你知道,“谁会关心这个涉及同性恋权利和计算机科学的交集呢?” 我觉得在英国大概有 500 个人关心这件事……毕竟请愿只对英国居民开放。

果然,很快就有 500 人签名,我想:“我试试吧,看看能不能让媒体报道这件事。” 所以我一个人努力工作,试图让媒体写这件事。最终,《曼彻斯特卫报》报道了它。然后是《独立报》,接着事情开始有点发酵了。

第一个签名的名人是理查德·道金斯[16]。理查德·道金斯签名的好处是(译者注: Dawkins calls for official apology for Turing[17]),我可以回到媒体那里说:“我知道我之前告诉过你这个请愿,但现在理查德·道金斯签名了,所以你应该写的是‘理查德·道金斯签了这个请愿’。” 我就这样做了,然后我写了这个很酷的脚注---当时签名的人名是公开的,我父亲每天都在读这些名字并告诉我:“我觉得这个人是某个名人。” 最终,我把我父亲(做的事情)自动化了。

我写了一个很丑的 Perl 脚本,它会获取名字,并在维基百科上搜索,看看这个人有没有维基百科页面,如果页面上写着“某某是英国人/苏格兰人/英格兰人/威尔士人”之类的东西,尝试判断他们是不是英国的知名人士。如果是,它就会给我发邮件,然后我可以联系他们询问……

我就是这样联系到作家伊恩·麦克尤恩[18]的。他的名字在签名名单上,我找到了他的电子邮件地址,发邮件问他:“你是签名的人吗?” 他说:“是的。” 然后我说:“我可以告诉媒体吗?” “当然可以。” 我就这样做了,请愿活动一步步扩大。

八月底的银行假期前,我给 BBC 的一个记者 Zoe Kleinman 发了封邮件。我之前写过一本书,她写过一些关于我书的文章……我给她发了一封非常大胆的邮件,说:“这是一个非常重要的故事。你应该写这篇报道。” 我在告诉 BBC:“快点写吧。” 她很客气地回信说:“你知道吗,我会写点什么。我要去度银行假期;我会把文章提交给编辑,但我不知道会不会发表。” 她确实写了,我却没看到它。

周日晚,我睡觉了,银行假期周一醒来时,我看着签名的数量,几乎像是一条垂直线,因为突然有成千上万的人在签名……因为 BBC 有着巨大的影响力。如果 BBC 说这是一个重要的事情,人们就会关注它,全球的媒体也会转载……之后我上了电视,到处谈论这个话题。那时已经有 3 万人签名了。

Mat Ryer: 我就是其中之一,John。

John Graham-Cumming: 谢谢你,谢谢你签了这个请愿。

Mat Ryer: 我没有被你自动化的父亲标记为知名人士。

John Graham-Cumming: 是的,很抱歉……

Mat Ryer: 没关系。我希望你已经删除了那个程序……[笑声]

John Graham-Cumming: 我把这件事的所有内容都发布在了我的博客上。

Mat Ryer: 我会提交一个 Pull Request,感谢……[笑声] 把我加进去。

John Graham-Cumming: 把你自己加进去吧。[笑] 我不确定它现在还能不能用了,可能有点晚了。不过……然后之后我得了流感,病得很厉害……

Mat Ryer: 哦,天哪……

John Graham-Cumming: 我躺在床上,想着:“我得看看我的邮件,可能工作上有事。” 我查了我的个人邮箱,里面有封邮件,是一个我完全不认识的女人发来的,她说:“你能打电话给唐宁街 10 号吗?这是号码。”

Mat Ryer: 哇哦。

John Graham-Cumming: 我当时以为肯定是搞错了,不过我还是在网上查了下号码,果然是 10 号唐宁街的总机。所以想象一下,我躺在床上,昏昏沉沉的,拨通了这个号码,告诉他们我的身份,立刻就被接通。对方说:“道歉声明今晚就要发布了。我们已经把它提交给《每日电讯报》。” 声明会在那里发表。她说:“我现在要读给你听,你得确认同意。”

她在电话里读给我听,我觉得写得很好……这是大家都能看到的文本。然后她说:“戈登想和你说几句话。” 当时的戈登是首相戈登·布朗……

Mat Ryer: 真的?

John Graham-Cumming: 是的……她说:“戈登想和你说话。” 我说:“好吧……” 然后她说:“他会打电话给你。”[笑声] 我挂了电话,坐在那里……这下我完全清醒了,想着:“天哪,这真要发生了吗?” 然后我的手机响了,居然是戈登·布朗。他没有任何铺垫,没人打电话来说:“这是首相。你准备好了吗?” 电话一接通就是:“你好,John,我是戈登……” 你可以想象,戈登·布朗不是一个健谈的人,而我又得了流感……[笑声] 所以我们俩在电话里都不太想多说话……

他对我说的第一句话---我永远不会忘记---他说:“你好,John,我是戈登。我想你知道我为什么打电话给你……” 我当时心想:“天哪,幸好我知道你为什么打电话给我!我肯定缴了所有的税,也肯定从没违规停车过……” 然后我们有了一段非常拘谨的对话,因为我感觉很糟糕,而他是个很严肃的人……就这样。那就是我和布莱切利公园的渊源。之后,当然,图灵得到了更多的认可,我们也可以庆祝他的贡献,我觉得这也为布莱切利公园带来了更多关注。

Mat Ryer:  是的。你发起的请愿是为了道歉,我觉得这很合适。后来他们赦免了他。对我来说,这意味着某些地方他们做错了什么。所以赦免我觉得不太合适,但道歉绝对是对的。

John Graham-Cumming: 我也是。我不支持赦免运动。有两件事对我来说很重要。首先,我觉得道歉很好,因为他们不仅向图灵道歉,还向所有被定罪的人道歉。这很重要。第二件事是联合政府---卡梅伦的政府---引入了一项《保护自由法》。这项法律废除了同性恋行为的犯罪化……问题是,今天仍然有一些人因为这个事情被定罪---这些罪名从未被清除---因为当时的法律是非法的。《保护自由法》解决了这个问题。我觉得这很好,我也觉得自己不需要做更多了。我已经做得比我想象的多了。

后来就出现了赦免的想法,实际上我对赦免很不满,因为只赦免了图灵……有人主张赦免的理由是他是个天才,所以他应该得到赦免……但我觉得这是错误的。要么你赦免所有人……最终他们确实赦免了所有人,所以这还好,但这个想法让我不安。首先是赦免,因为我不认为他做错了什么;其次是单独赦免他,这也是个错误。我觉得戈登·布朗的道歉做得很好,我很高兴我做了这件事。

Mat Ryer: 是的,我也很高兴他道歉了。谢谢你做了这件事。我不知道你付出了这么多努力。我原以为这只是一个病毒式传播的请愿活动……但你这么努力,我更感激了。谢谢你分享这个故事。

Jaana Dogan: 是的,太棒了……过去十年里,艾伦·图灵在流行文化中的代表性大大增加,这都要归功于你。谢谢你。

John Graham-Cumming: 我想是的。虽然很难把所有功劳都归于自己,但如果你看看,确实在道歉之后事情才真正开始发酵。

Jaana Dogan: 没错,对。

Mat Ryer: 我认为这是你的功劳,John。虽然我不是你父亲标记的知名人士,但你仍然得到了……事实上,我在我的书《Go 编程蓝图》中提到了这件事。依然在售。

顺便说一下,John,我们还有另一个联系……我赢得了你那本《极客地图》书的副本,当时你有一个小测验,涉及到电视剧《囚徒》的一个引用。我不知道你是否记得……你曾说每当某个网站要求你输入生日时,你都会给同样的假生日,对吧?

John Graham-Cumming: [笑] 是的,看来你也是个巨大的极客。

Mat Ryer: [笑] 是的。

John Graham-Cumming: 在推特上---推特认为我的生日是 1928 年 3 月 19 日,这是《囚徒》电视剧中主角的生日,也是扮演他的演员的生日……所以,是的,干得好。

Mat Ryer: 谢谢,非常感谢你。[笑声] 好了,时间差不多了。John,非常感谢你加入我们,告诉我们关于 Cloudflare 和你在那里的 Go 经验。这真的很棒。

John Graham-Cumming: 谢谢你们。

Mat Ryer: 也谢谢 Jaana 和 Jon。这次真的很棒。下次见。

参考资料
[1]

Go at Cloudflare: https://changelog.com/gotime/113

[2]

Jaana: https://github.com/rakyll

[3]

Jon: https://github.com/joncalhoun

[4]

Mat: https://github.com/matryer

[5]

John Graham-Cumming: https://github.com/jgrahamc

[6]

Communicating Sequential Processes 论文: https://www.cs.cmu.edu/~crary/819-f09/Hoare78.pdf

[7]

Cloudflare is deprecating Railgun: https://blog.cloudflare.com/deprecating-railgun/

[8]

GopherCon 2014 A Channel Compendium by John Graham-Cumming: https://www.youtube.com/watch?v=SmoM1InWXr0

[9]

Unimog: https://blog.cloudflare.com/unimog-cloudflares-edge-load-balancer/

[10]

Quicksilver: https://blog.cloudflare.com/introducing-quicksilver-configuration-distribution-at-internet-scale/

[11]

Matthew: https://www.linkedin.com/in/mprince

[12]

blog.cloudflare.com: https://blog.cloudflare.com

[13]

GopherCon UK: https://www.gophercon.co.uk/

[14]

布莱切利公园: https://baike.baidu.com/item/%E5%B8%83%E8%8E%B1%E5%88%87%E5%88%A9%E5%9B%AD/5162328

[15]

Stephen Fry 的一条推文: https://twitter.com/stephenfry/status/2292753666

[16]

理查德·道金斯: http://en.wikipedia.org/wiki/Richard_Dawkins

[17]

Dawkins calls for official apology for Turing: https://www.independent.co.uk/news/people/news/dawkins-calls-for-official-apology-for-turing-1774033.html

[18]

伊恩·麦克尤恩: https://baike.baidu.com/item/%E4%BC%8A%E6%81%A9%C2%B7%E9%BA%A6%E5%85%8B%E5%B0%A4%E6%81%A9/6815553


旅途散记
记录技术提升