acm-header
登录

ACM通信

实践

应用程序框架


扇形平原,插图

图片来源:Korkeng / Shutterstock

回到顶部

共享库鼓励代码重用,促进团队间的一致性,并最终提高产品的速度和质量。但是应用程序开发人员仍然需要选择正确的库,确定如何正确地配置它们,并将所有内容连接在一起。通过预安装和预配置库,应用程序框架提供了简化的开发人员体验,甚至更大的一致性,尽管以牺牲一些灵活性为代价。

通过拥有整个应用程序生命周期,框架超越了仅仅是库的集合。有保证的框架行为可以扩展开发—例如,通过避免对每个应用程序进行深入的安全性或隐私代码检查。框架提供的跨团队和跨语言的一致性也是更高级别自动化和智能系统的必要基础。

本文概述了框架的核心方面,然后深入探讨它们的好处、它们需要的权衡,以及我们建议实现的最重要的特性。最后,我们在谷歌上展示了一个框架的实际应用:开发一个微服务平台如何允许谷歌打破其单一的代码库,以及框架如何实现这种改变。

在许多方面,框架与共享库类似,并具有类似的优点。对于谷歌,两个技术原则有助于区分框架和库:控制倒置和可扩展性。虽然本文中所讨论的框架的许多优点看起来并不大,但它们主要来源于这些原则。

控制反转。在从头构建的应用程序中,工程师决定程序的流程正常控制流。在基于框架的应用程序中,框架控制流并调用用户代码——这是反向控制流。反向控制流有时被称为好莱坞原则:“不要打电话给我们;我们会打电话给你。”框架控制流在所有应用程序中都有良好的定义和标准。理想情况下,应用程序只实现特定于应用程序的逻辑,而框架处理构建类似微服务的所有其他细节。

可扩展性是库中的第二个关键微分器,与控制的反转同时进行。因为框架的控制流是由框架所有的,所以改变其行为的唯一机制是通过它公开的扩展点。例如,服务器框架可能有一个扩展点,该扩展点允许应用程序在每个请求之后运行一些代码。这种行为还意味着框架的非可扩展部分是固定的,不能由应用程序更改。

回到顶部

框架的好处

除了共享库提供的功能之外,框架还有许多好处,它们以不同的方式对各种涉众都有好处。

开发人员。最终决定是否使用可用框架的开发人员是他们最明显的受益者。开发人员的主要好处包括提高生产力、简单性,以及符合最佳实践。通过利用内置的框架特性,开发人员可以编写更少的代码,而且他们编写的代码也会更简单,因为框架会处理样板代码。框架通过提供合理的默认值并消除无意义和耗时的决策,为最佳实践提供了一条明亮的路径。

生产团队。除了提高开发人员的生产力之外,框架还通过释放团队资源而使产品团队受益,否则这些资源将用于构建冗余的基础设施。然后产品团队就可以专注于让他们的产品与众不同的地方。

当框架将产品团队与底层基础架构的变更隔离时,产品团队也会受益。虽然在所有情况下都不可能,但框架提供的附加抽象意味着可以将一些基础设施迁移视为实现细节,这些细节完全由维护框架的人处理。

以谷歌启动的产品通常需要多个团队的批准。例如,发射协调工程师负责审查发射的生产安全和有效性,而信息安全工程师将检查应用程序的设计,以找出常见的安全漏洞。当执行审查的团队熟悉框架并依赖于它们的行为保证时,框架可以简化启动审查过程。上线后,标准化也将使系统更易于管理。

这家公司。在公司层面,通用框架可以通过减少开发人员熟悉新应用程序所需的时间来提高开发人员的移动性。如果一个公司有足够大的开发人员社区,投资于高质量的文档和培训计划是值得的;这反过来又有助于吸引来自社区本身的文档和代码贡献。框架的广泛使用还意味着,对框架的改进进行相对较小的投资就可以产生较大的影响。

随着时间的推移,框架建筑中的集中化可以对不断变化的景观做出广泛的反应。例如,如果您依赖于一致的微服务/远程过程调用(RPC)框架,并且带宽相对于CPU变得更昂贵,那么框架的默认值可以基于这种成本权衡集中调整压缩参数。

回到顶部

权衡的框架

虽然框架具有刚才描述的多种优点,但它们也需要进行一定的权衡。

固执己见的框架会阻碍创新。框架常常必须选择支持哪种类型的技术。虽然支持每一种可能的技术是不现实的,但当框架实现时,就会有明显的好处固执己见的也就是说,当他们鼓励使用某些技术或设计模式而不是其他的时候。


虽然应用程序可以利用框架扩展点,但大多数应用程序代码都采用动作的形式,这些动作包含特定于应用程序的业务逻辑。


固执己见的框架可以极大地简化开发人员使用新系统时的工作。当开发人员有许多方法来完成相同的任务时,他们很容易陷入对整个系统影响很小的决策细节中。对于这些开发人员来说,接受一个固执己见的框架的首选技术允许他们专注于构建他们的系统的业务。拥有共同和一致的偏好对整个公司也有好处,即使这个答案并不完美。

当然,您可能不得不处理大量的应用程序和团队,一些产品需求或团队偏好可能不太适合现有的框架。框架维护者需要决定什么是最佳实践,什么不是最佳实践,以及一个非常规的用例是否“有效”,这可能会让所有相关人员感到不舒服。

另一个重要的考虑因素是,即使某件事在今天显然是最佳实践,但技术发展很快,存在框架无法跟上创新步伐的风险。试验替代的应用程序设计可能成本更高,因为开发人员要么需要了解框架实现的细节,要么需要依赖框架维护者的帮助。

通用性会导致不必要的抽象。只有当大量应用程序使用同一框架时,才能实现框架的许多优点,例如公共控制界面(后面会解释)。这样的框架必须足够通用,以支持绝大多数用例,这在实践中意味着拥有丰富的请求生命周期和任何应用程序都需要的所有扩展挂钩。这些需求需要在应用程序和底层库之间添加一些间接层,这会增加认知开销和CPU开销。对于应用程序开发人员来说,软件栈中的更多层会使调试复杂化。

框架的另一个潜在缺点是,它们是工程师必须学习的另一件事。新聘用的谷歌员工经常被他们需要学习的大量技术所淹没,仅仅是为了让一个“你好,世界”的例子发挥作用。功能齐全的框架可能会使情况变得更糟,而不是更好。

谷歌通过使每个框架的核心尽可能简单和高性能,并将其他特性作为可选模块,在一定程度上缓解了这些问题。谷歌还试图提供与框架相关的工具,这些工具可以利用框架的固有结构来简化调试。然而,框架最终有一个您必须承认的成本,您需要确保任何给定的框架提供足够的好处来证明这个成本是合理的。不同的编程语言框架也可能有不同的权衡集,从而为开发人员创建了另一个决策点和成本/收益场景。

回到顶部

重要的框架特性

如前所述,控制倒置和可扩展性是框架的基本方面。除了这些基本参数之外,框架还应该考虑其他几个特性。

标准化的应用程序生命周期。重申一下,控制倒置意味着框架拥有并标准化了应用程序的整个生命周期,但是这种结构实际上有什么好处呢?避免级联故障的场景提供了一个示例。

级联故障是引起系统中断的一个众所周知的原因,包括谷歌处的许多中断。当分布式系统的一部分发生故障时,就会出现故障,从而增加其他部分故障的概率。有关级联故障的原因以及如何避免它们的更多信息,请参阅“处理级联故障”一章网站可靠性工程。1

谷歌上的服务器框架有许多内置的防止级联故障的保护措施。其中两个最重要的原则是:

  • 保持服务。如果服务器能够成功应答请求,它就应该这样做。如果它能够成功地为某些类型的请求提供服务,但不能为其他类型的请求提供服务,那么它应该继续运行并响应它能够服务的请求。
  • 快速启动。服务器应该尽可能快地启动。更快的启动意味着更快的从崩溃中恢复。服务器应该避免连续等待涉及到外部系统的rpc的初始化完成。

谷歌生产环境为每个服务器提供了可配置的恢复“健康”的时间(开始响应请求)。如果超过时间,系统认为发生了不可恢复的错误,并终止服务器进程。

在没有框架的情况下,自然会出现一种常见的反模式:库创建自己的RPC连接,然后等待该连接准备就绪。随着服务器代码库的不断增长,最终在传递依赖项中可能会有几十个这样的库。结果是服务器初始化代码,如果有效展开,将会是这样的图1

f1.jpg
图1。服务器初始化代码。

在正常情况下,这段代码可以正常工作,这尤其成问题,因为没有迹象表明潜藏的问题。当一个相关的后端服务变慢或完全停止时,这个问题就会显现出来——现在主服务器的启动被延迟了。如果启动被充分延迟,它将在有机会开始处理请求之前被终止,这可能会导致级联失败。

一种可能的改进是首先创建RPC存根,如图2,然后平行地等待它们。在这个场景中,您只需要等待马克斯的初始化时间,而不是求和。

f2.jpg
图2。RPC存根。

虽然仍然不完美,但即使这种有限的重构也证明了您需要一些创建RPC存根的库之间的协调——它们必须将等待存根的责任移交给库之外的东西。在谷歌的情况下,这个责任是由服务器框架拥有的,它还具有以下特性:

  • 等待所有存根准备就绪并行,通过定期轮询准备(< 1秒)。一旦过了可配置的超时时间,服务器就可以继续初始化即使不是所有的后端都准备好了。
  • 发出人类和机器可读的日志,用于调试和与标准监视和警报系统集成。
  • 通过通用机制插入任意资源,而不仅仅是RPC存根。从技术上讲,只有一个返回布尔值(表示“我准备好了吗?”)的函数和一个名称对于日志记录是必要的。这些钩子通常被用于处理资源的公共库(例如,文件API);应用程序开发人员通常只需通过使用库就能自动获得行为。
  • 提供一种集中的方式将某些后端配置为“关键”,这将改变它们的启动和运行时行为。

对于任何单独的库来说,这些特性(正确地)被认为是多余的,但是如果您可以在一个中心位置实现它们,那么所有后端使用的库都可以从中受益,那么实现它们是有意义的。正如共享库是在应用程序之间共享代码的一种方式一样,在这种情况下,框架是库本身共享功能的一种方式。

站点可靠性工程师(SREs)更乐于支持基于框架的服务器,因为这些特性,他们经常鼓励他们的开发伙伴选择基于框架的解决方案。框架提供了生产规则的基线水平,这是很难(如果不是不可能的话)在仅仅将一堆断开的库粘合在一起时实现的。

标准化的请求生命周期。虽然细节取决于应用程序的类型,但许多框架支持整个应用程序生命周期之外的其他生命周期。对于谷歌服务器框架,最重要的工作单元是请求。遵循类似的控制反转模型,请求生命周期的目标是将请求不同方面的职责划分为单独的可扩展代码段。这使得应用程序开发人员可以专注于编写使其应用程序独特的实际业务逻辑。

下面是一个真实世界框架及其组件的示例,如中所示图3

f3.jpg
图3。一个示例框架及其组件。

  • 处理器-拦截传入和传出的有效载荷。主要用于日志记录,但也有一些使请求短路的功能(例如,在整个应用程序中强制使用不变量)。
  • 动作-应用程序业务逻辑,接受请求并返回响应对象,可能有副作用。
  • 异常处理程序——将未捕获的异常转换为响应对象。
  • 响应处理程序——将响应对象序列化到客户机。

虽然应用程序可以利用框架扩展点,但大多数应用程序代码都采用动作的形式,这些动作包含特定于应用程序的业务逻辑。

例如,这种关注点分离在Web安全领域就很有帮助。谷歌开发了许多Web应用程序,因此它非常希望防范所有各种Web安全漏洞,例如XSS(跨站点脚本编制)。XSS漏洞通常是由应用程序代码返回的字符串响应引起的,其中包含未充分清理或转义的数据。修复这些错误的传统方法是简单地添加丢失的转义数据,并希望测试和代码审查能够防止未来出现类似的问题(破坏者:他们不会)。

从根本上说,这种方法是行不通的,因为应用程序所针对的底层api天生容易出现bug,比如XSS,因为它们接受字符串或类似的非结构化/非类型化数据。例如,Java Servlet API为应用程序提供了一个原始编写器,您可以向它传递任意字符。这种方法给开发人员带来了太多的负担,使他们无法做正确的事情;相反,谷歌的安全团队专注于设计天生安全的api,例如:

  • 具有上下文感知转义的HTML模板系统。
  • 抗SQL注入的数据库api。
  • “安全HTML”包装器类型携带约定,规定它们的值在各种上下文中使用都是安全的。

谷歌的服务器框架的请求生命周期补充了这些api的使用,因为应用程序代码从不处理原始字符串或字节。相反,代码返回具有SafeHtmlResponse等类型的高级响应对象,这些对象只能以保证格式良好的方式构造。将这些响应对象转换为网络上的字节是a的职责响应处理程序,它通常是框架的内置部分。谷歌有时需要自定义响应处理程序,但必须由安全团队审查所有用法——这是在构建级强制执行的要求。

最终的影响是谷歌将使用这些框架的应用程序中的XSS漏洞数量减少到几乎为零。正如您可以想象的那样,谷歌的安全团队强烈鼓励使用框架,并为改进所有框架用户的安全性做出了许多框架贡献。标准的基于框架的服务器可以有效地跳过定制服务器启动时需要进行的许多安全或隐私检查,因为框架可以保证某些行为。

当然,结构化和可扩展的请求生命周期的好处远不止将业务逻辑从响应序列化中分离出来。最基本的好处是,它使每个组件都很小,易于推理,这有助于长期的代码健康。谷歌中的其他基础设施团队可以轻松扩展框架功能,而无需直接与框架团队合作。最后,应用程序可以引入它们自己的横切特性,而不需要触及每个操作。在某些情况下,这些特性是特定于领域的,但其他特性最终是普遍适用的,并最终“上流”到框架本身。

常见的控制面。我们所说的控制面将二进制文件的所有非应用程序特定的输入和输出视为黑盒。这包括操作控制、监视、日志记录和配置。

服务器间的一致性简化了故障排除。不管他们故障排除在哪个服务器上,开发人员和SREs都知道哪些信息是可用的,以及在哪里可以找到这些信息。如果某个服务器需要调整,每个人都知道哪些按钮是可用的,以及如何更改它们。

除了使人类更容易操作服务器外,跨服务器拥有共同的控制界面也使共享自动化成为可能。例如,如果所有服务器都以标准方式导出错误,那么更改发布管道以执行自动canar附带就成为可能:您可以首先向一些服务器推出一个新的二进制文件,并在执行更广泛的推出之前寻找错误峰值。你可以在“演进的SRE参与模型”一章中从SRE的角度阅读更多关于通用控制界面的好处。1

框架提供了一个很好的机会来强制应用程序控制表面的一致性。虽然,通常只有少数人关心控制表面的确切组成,但拥有一个单一的、一致的答案对公司来说有巨大的价值。一致性意味着您可以轻松地跨多个二进制文件共享和扩展自动化。通过简化与周围生态系统的集成,您可以获得的好处一个标准的很多倍,独立于标准本身的优点。

实现公共控制界面的一个挑战是,框架维护者经常是第一个发现编程语言库之间不一致的人。例如,所有语言都有一个命令行参数的现有概念,其值为持续时间。从积极的方面来看,语言之间的语法在一定程度上是兼容的,至少对于最基本的例子来说是这样,比如“1h30m”。然而,一旦我们深入研究细节,就会看到不同的画面,如图所示图4

f4.jpg
图4。编程语言库之间的不一致性。

如今,图书馆业主们更加意识到跨语言一致性的价值,以及考虑这种一致性的必要性。在框架端,谷歌还使用测试工具对用每种编程语言编写的服务器运行相同的测试套件,以确保一致性。

模块化。不管是好是坏,谷歌没有核心的软件工程权威。尽管大多数开发人员针对单一的代码存储库工作,但是工程实践在团队之间仍然有很大的差异。对于任何给定的项目,技术的选择通常取决于项目的技术领导,很少有自上而下的要求。可以理解的是,人们倾向于选择他们有经验的技术。因此,为了让一项新技术获得广泛采用,它必须要么具有明显的价值,要么具有较低的进入门槛;通常情况下,两者都必须具备。

对于谷歌的服务器框架,核心生命周期管理和请求调度是唯一严格要求的特性。所有其他功能都捆绑到可选的、独立的“模块”中,这些模块使用框架公开的各种生命周期挂钩实现其功能,如前所述。应用程序开发人员可以选择将哪些模块添加到他们的服务器中,在许多情况下,甚至可以通过一行代码添加主要特性:

安装(新LoadSheddingModule ());

可用的标准模块的实际列表有数百个,包括身份验证、实验和日志记录等特性。

逐步向服务器添加框架特性的能力是谷歌采用框架的一个重要因素。它允许“Hello, world”示例和原型服务器既小又容易理解,同时还可以在适当的时候简单地扩展到功能更齐全的服务器。

模块的独立性还允许在您有特殊需求时轻松地将特定于应用程序的模块替换为标准框架模块。因为标准框架模块与特定于应用程序的模块使用相同的可扩展性api,所以将一个有用的特性上传到框架中通常是一件移动代码的小事。这允许一个框架成为一个不断增长的最佳实践集合,一旦它们已经证明了它们在现实世界中的价值。

这里展示的高度封装意味着框架维护者可以从根本上改变模块的实现,而不需要修改任何应用程序代码。当后端系统被弃用或需要更改API(这是非常常见的)时,这尤其有用。谷歌框架使许多应用程序开发人员无需执行复杂或昂贵的迁移,对于许多团队来说,这是当今框架最引人注目的好处之一。

框架维护者的一个角色是确保模块之间正确协作。维护人员还可以选择默认的模块列表,或者提供关于哪些模块应该用于不同情况的建议和约束。一个挑战是在粒度方面取得正确的平衡:虽然开发人员倾向于为了灵活性而选择细粒度的模块,但对于框架维护者来说,要确保所有组合都能很好地一起工作是比较困难的。

Microservices。框架的广泛使用所提供的标准化为更高级别的工具和自动化提供了机会。这允许谷歌创建一个微服务平台,并拆分单片服务器。

微服务出现之前:庞然大物。共享库和框架的存在大大简化了在谷歌中编写具有生产质量的代码的实际操作。然而,编写代码只是在谷歌上部署应用程序的一部分。其他关键因素包括集成测试、对安全性和隐私等方面的启动审查、获取生产资源、执行发布、收集和保存日志、试验以及调试和解决中断。

过去,处理所有这些项是一个昂贵的过程,所有服务器所有者都必须完成,无论服务器大小如何。因此,较小的团队不需要部署新服务器,而是需要添加新服务现有的服务器,他们可以添加他们的代码。这样,团队就可以专注于编写他们的业务逻辑,并“免费”获得其他所有东西。当然,一旦有足够多的团队采用这种方法,很明显,利用现有的服务器功能实际上并不是免费的。这种激励结构多次导致了谷歌的公地悲剧:支持良好的服务器不断增长,直到它们变成巨大且不可维护的庞然大物。

巨石有很多负面影响。在开发人员生产力方面,您必须处理缓慢的构建、缓慢的服务器启动,以及当您试图提交更改时,您的预提交测试很可能会中断。例如,一个重要的谷歌搜索相关的c++二进制文件变得如此之大,以至于在当时的技术限制(12GB RAM)下连链接都不可能。

当涉及到发行时,我们很难按照计划将它们推向真正的巨石。随着一个庞然大物的增长,贡献开发人员的数量也在增长,这就导致了更多的阻塞bug。延迟的发布可能会使实现下一个发布更加困难,从而形成一个恶性循环。

在生产过程中,庞然大物在表面上不相关的服务之间创造了一个危险的共同命运,以及由意外交互引起的更大的bug机会。相互独立地扩展服务是不可能的,这增加了资源分配的难度。

远离面向服务器的世界。虽然最终很清楚谷歌这样的局面是不可持续的,但是没有好的替代方案。简单地命令人们停止在巨石上加力也会产生同样糟糕的后果。相反,谷歌需要消除生产和运行新服务器的麻烦。这样就可以做出决定服务应该编一个服务器纯粹基于生产原因,而不是开发人员方便,如图5

f5.jpg
图5。将一个单片服务器分解成更小的服务器。

从开发人员应该只关注其特定于应用程序的服务的业务逻辑这一目标向后追溯其他的一切应该尽可能地自动化,一些需求最终变得清晰起来:

  • 开发人员应该声明和实现服务api,而不是编写主要方法:编排二进制文件如何实际运行是微服务平台的职责。
  • 自动化所需的所有元数据(包括生产配置)都应该与服务代码一起以声明格式存在。
  • 服务之间的资源和依赖关系应该是显式和声明式的。理想情况下,您应该能够通过查看服务范围的元数据来可视化整个生产拓扑。
  • 服务应该彼此隔离,这样任意的服务就可以组合到一个服务器中。

在其他要求中,这意味着避免全局状态和副作用。

当这些需求得到满足时,几乎所有以前手工的过程都可以自动化。例如,在运行集成测试时,测试基础设施可以使用元数据连接服务依赖关系图的一部分。

使用这些原则的微服务平台是在谷歌上开发的,最初的目的是打破一个特别大的单片服务器,该服务器由于密集的特性开发而快速增长。一旦该平台被证明是有益的,它就被谷歌内的其他团队有机地采用,并最终分离成一个独立的、得到官方支持的项目。

今天,这个平台已经成为新服务器开发的事实上的标准,部分原因是它对小型团队和大型组织都很有吸引力。由于高水平的自动化,小团队现在可以在几天内轻松地提供google质量的产品服务,而在此之前,按照最佳实践可能需要数月的时间。对于大型组织,跨团队的一致性降低了支持成本,并且共享平台意味着组织特定的基础架构团队的人员配备通常是不必要的。

转向微服务的另一个好处是鼓励开发人员更多地考虑服务之间的适当分工,这导致了更合理的系统架构。使用gRPC和协议缓冲区等技术作为独立系统之间的边界,会迫使您以一种只在同一进程中使用函数调用时不一定会发生的方式来考虑api。RPC系统也是语言无关的,因此每个微服务所有者都可以独立决定使用哪种语言。

剩下的一个挑战,也是未来工作的成熟领域,就是提供更高级别的工具来管理数量不断增长的微服务。例如,在上一个时代编写的监视控制台可能使用了相对较少的唯一二进制文件,这将需要一个新的用户界面来适应当人们完全采用微服务时产生的大量二进制文件。

与框架之间的关系。框架是谷歌微服务平台工作的关键组件,原因如下:

  • 框架生命周期中固有的控制倒置自然适合于这样一种模型,即应用程序开发人员只需将其服务实现移交给平台。
  • 许多平台特性都需要公共控制服务(跨服务器和语言),包括发布管理、监视和日志。
  • 模块化意味着平台和应用程序代码都可以提供独立的模块,当它们组合在一起时,可以以一种健全的方式工作,形成一个完整的服务器。

图6展示了谷歌完整微服务平台的完整开发栈。

f6.jpg
图6。谷歌微服务平台开发栈。

如前所述,框架可以提供比库更高级别的封装,这简化了应用程序的编写,并隔离了底层库的混乱。以类似的方式,微服务平台超越了仅仅封装其他构件(如生产配置)的代码。这允许相应的更高级别的简化和分离。例如,平台维护者可以(如有必要)自动应用紧急代码修复或配置更改,重新构建所有受影响的二进制文件,并以统一的方式将它们推向生产——这在以前是不可能的。

然而,使用微服务平台确实存在一些挑战。其中最大的一个问题是,强制使微服务平台正常运行所需的所有不变量可能非常麻烦,甚至可能影响应用程序的编码方式。举个例子,谷歌的Java服务器共享某些线程池。结合所有服务必须彼此隔离的要求,这意味着不能允许阻塞线程每个请求的模型——阻塞服务很容易耗尽所有线程并饿死另一个服务。由于这个原因,服务器被要求只能是异步的,这个解决方案并不是所有团队都满意的。

另一个挑战是在微服务之间添加更多的跳跃可能会增加整个请求的延迟。在某些情况下,这种延迟可以通过重写微服务的体系结构改进来缓解。对于其微服务平台,谷歌还确保恰好位于同一服务器中的服务之间的请求使用优化的进程内传输。

回到顶部

结论

虽然框架可能是一个强大的工具,但它们也有一些缺点,并不是对所有组织都有意义。框架维护者需要提供标准化和定义良好的行为,但不能过于规定性。然而,当框架达到适当的平衡时,它们可以大大提高开发人员的生产力。框架的广泛使用所提供的一致性对其他团队来说是一个福音,例如SRE和安全性,它们对应用程序的质量有既得利益。此外,框架的结构为构建更高层次的抽象(如微服务平台)提供了基础,从而为系统架构和自动化提供了新的机会。在谷歌上,这样的框架和平台得到了广泛的有机采用,并产生了显著的积极影响。

回到顶部

参考文献

1.B.等。网站可靠性工程。O reilly Media, Inc.(2016年4月)。

回到顶部

作者

克里斯Nokleberg是谷歌服务器框架的首席软件工程师和技术负责人。大约10年前,他作为谷歌Docs的技术领导者开始开发框架;他现在正在帮助大型团队采用谷歌的微服务平台,并在公司范围内标准化开发人员的最佳实践。

布拉德·霍克斯是谷歌负责核心基础设施的高级软件工程师。他在Java虚拟机的服务器框架上工作,该框架用于跨越谷歌的数千台服务器。


版权由作者/所有者持有。授权给ACM的出版权。
请求发布的权限permissions@acm.org

数字图书馆是由计算机协会出版的。版权所有©2021 ACM, Inc。


没有发现记录

登录为完全访问
»忘记密码? *创建ACM Web帐户
文章内容:
Baidu
map