Google和eBay在建设微服务生态系统中的深刻教训

Google和eBay在建设微服务生态系统中的深刻教训

当我们观察市场上的大规模系统,它们的系统架构往往都进化为非常相似的:一系列多语言聚合的微系统。比如Google、Twitter、eBay和Amazon。

那么在这种多语言聚合的微系统环境中工作的看法是如何呢?Randy Shoup,作为一个曾经在Google以及eBay高层工作过的过来人,关于Google和eBay在服务架构收缩性的经验有一些有趣的想法分享

我非常喜欢Randy演讲的是,他善于引导沉浸式的思维帮助听众去感受那些可能从来没有经历过的,比如创造、使用、延续和保护大规模的系统架构。

在Randy的生态服务领域演说中,他对生态部分提了一个问题:一个大规模的多语言聚合微服务生态系统到底是什么样的?

  • 对于运维服务的问题是:运维这样的服务对于服务提供者是什么样的感受?
  • 对于开发服务的问题是:当你作为一个服务的所有者是什么样的感受?
  • 对于反面模式服务的问题是:哪些是可能出错的?

这是一个非常有效的方法。

整个演说的亮点对于我来说是关于激励机制的想法,一个一致且贯穿始终的主题。虽然从未明确地作为一个单独的策略被提出。这是一切的背后隐藏激励因素,包括为什么我们需要小型团队、并且开发小且简洁的服务;为什么内部服务按服务器分配策略如此有效;系统架构是如何在没有架构师的情况下进化;简洁的设计如何自底向上进化;以及标准规则如何在没有中央委员会的管控下进化。

我的理解是在实施此类激励机制之前,我们必须非常深思熟虑。因为我们将要做的是基于大型且动态的代码基础,来调整大型且动态的组织架构。假如激励方向正确,我们并不需要非常明确的管控就可以轻而易举的实现目标。同样的,我们也可以更高效的在分布式系统下工作,假如我们能够消除那些相互依赖锁,不必依赖状态共享、消息机制、等来同步所有东西。

让我们来看看在现代开发模式下的大型可伸缩性系统是如何构建的

多语言聚合微服务是游戏终结者

  1. 大型系统的最终演变大多很相似:一系列多语言微服务。多语言指可以用不止一种以上语言来实现的每个小微服务。
  2. eBay从1995年开始至今已经演化到了第五代系统架构
    • 刚开始是整体Perl实现,由创始人在1995年的一个劳动节周末完成
    • 接下来是迁移到整体C++实现,一共包含三百四十万行左右的单一动态链接库
    • 基于之前的经验,接下来用java迁移到了更加分布式分区的系统架构下
    • 今天的eBay仍有大量的java代码存在,但是一系列的多语言微服务正在崛起
  3. Twitter的演化非常的类似,基本上他们已经有过三代不同的系统架构
    • 刚开始是整体Ruby on Rails实现
    • 然后前端是Javascript和Ruby的结合,以及大量Scala在后台
    • 最终他们迁移到了我们今天所看到的一系列多语言微服务架构
  4. Amazon也是走的同样的路子
    • 开始是整体的C++实现
    • 然后服务转向Java和Scala
    • 最终走向一系列多语言微服务架构

服务的生态系统

  1. 一个大型的多语言聚合微服务生态系统到底是怎么样的?
  2. 当前有成百上千个独立的服务工作在一起支撑着eBay和Google的业务
    • 现代大型系统更倾向于图状结构,而非树状层级结构
    • 当服务被其他很多服务所依赖的同时,其他很多服务也会依赖于它
    • 较老的大型系统典型架构遵循严格的层级结构

服务生态系统如何创建?

  1. 这些表现最好的系统架构更多的来自于产品的自我进化,而不是刚开始精巧的设计。比如在Google,从来没有过集中化控制的自顶向下设计,它纯粹是随时间自发成长起来的。
  2. 进化和自然选择。新的服务可以在任何有问题需要的时候被创建,或者说更多的是从已有的服务中淬炼出来。服务的生存期即是它的需求期,直到它不能再提供有效的使用价值为止才被弃用。
  3. 也有一些大型系统是由自底向上开发而来。简洁清晰的设计可以是进化偶发的结果而并非自顶向下设计的。
  4. 例如,下列一些分层服务的谷歌应用程序引擎。
    • 层次是非常清晰的。每一层都会增加一些并不包含在它下一层的内容,这并不是自顶向下的产品设计。
    • 这实际上是自底向上的实现结果。Colossus是Google的文件管理系统,所以它必须首先被创建。几年之后是Bigtable,然后再过几年是Megastore,之后几年才是云数据存储集成在Megastore之上。
    • 你完全不必要如此自顶向下的架构设计,就可以拥有像这般优秀的产品
  5. 这是没有架构师的系统架构。没有人在Google有架构师的头衔。这里也没有一个中心来管控技术走向。大多数的技术方案是由各个独立的本地团队自己决定,而不是全球总部的决策。
  6. 对比2004年的eBay。当时他们有一个架构评审委员会来负责所有的大型项目。
    • 通常当任何项目变更需求到达这个委员会被评审时已为时过晚
    • 这个集中化的评审结构成了一个瓶颈,经常性的它的唯一作用就是在最后一分钟说不
  7. 对于eBay来说一个更好的方法来处理这种情况是,把这些有经验的评审委员会专家的经验归档整理,并设计成可由各个团队重复使用的库文件或者一系列的指导文件,由此帮助团队独立决策而不是在最后一刻才提交给评审流程。

标准如何在没有架构师的情况下进化?

  1. 这是可能的,即在没有中心控制的情况下形成标准化
    • 标准化往往产生于各个服务和通用的基础设施通信过程中。
    • 标准之所以成为标准是因为它们比其他可选项更加合适。
  2. 通信部分往往已经标准化
    • 网络协议。Google所使用的自身协议名为Stubby,eBay所使用的是REST。
    • 数据格式。Google所使用的协议是Buffers。eBay更倾向于使用JSON。
    • 接口架构标准。Google使用Buffers。eBay使用的是JSON
  3. 通用基础设施部分往往也已经是标准化的。
    • 源代码控制。
    • 配置管理。
    • 群集管理器。
    • 监控系统。
    • 警报系统。
    • 诊断工具。
    • 以及所有这些组件演化出来的公共规约
  4. 在一个进化的环境中,标准是通过下列强制方式来得到的:编码,鼓励,代码审查,以及代码搜索。
    • 在实践中所鼓励的最好最简单的办法即实际编码。这不是自上而下的审查,或预先设计的,而是通过最简单直接的方式来把工作做好。
    • 激励的方式在于鼓励团队通过库函数形式来实现更高的代码复用
    • 激励也可以通过鼓励服务去支持对于更多协议的支持
    • Google非常著名的一点就是它的每一行代码在实际更新到代码库之前,都会被至少一个其他程序员审查过。这是一种非常号的通用沟通实践。
    • 在极端情况下,Google的工程师可以去搜索整个代码库。这意味着巨大的附加价值,当每个程序员试图理解如何完成任务时。基于超过一万个工程师的工作成果,如果你正在尝试做一些别人已经做过的类似的东西,这可以允许你基于代码库参考和学习最佳方案。当然,这也会帮助你避免前人已经犯过的错误。
  5. 鼓励共同的做法和规范的约定,让做正确的事更容易,做错误的事更困难。
  6. 每个服务相对于其他服务都是独立的个体
    • 在Google,内部服务之间并没有标准化的东西,每个服务对于外界都是一个黑盒。
    • 这里有约定和公共库,但是并没有语言上的限制。主要使用的是四种语言:C++,Go,Java,Python。以及其他大量的服务都是由多种不同的语言实现的。
    • 这里也没有标准化的周边框架或持久层机制。
  7. 在一个成熟的服务生态中,我们需要标准化规范的是大局,而非每个具体的点。我们只需要定义通用的交互方式,而非具体的实现。

创造新的服务

  1. 新的服务会在真正被需要时创造
  2. 通常一个功能因为某个特定用例所创造时,就已经证明该功能是普遍性且有用的。
    • 某个团队会建立并开始拆分这个服务,从而形成独立的单元。
    • 上述动作仅仅发生在当该功能是成功的并且适用于很多不同的用例时。
  3. 这些架构的成长完全基于实用主义。没有人能够坐在高位来指挥某些服务应该增加。
  4. Google的文件系统很早就支持搜索引擎。这当然毫不奇怪,因为分布式文件系统对这方面更加适应。
  5. Bigtable从一开始就支持搜索引擎,而且是非常广泛性的适用。
  6. Megastore被建立来作为Google应用程序的存储机制,而且是更加广泛性的适用。
  7. Google的应用程序引擎本身是由一小群工程师基于帮助网站建设的需要而启动的。
  8. Gmail是某项工程的副产品,首先是在公司内部超级方便实用,然后被开放给外界其他需要的人们。

卸载旧服务

  1. 当一个服务没有用了应该怎么办?
  2. 首先我们会复用那些仍然可以被改变使用用途的服务。
  3. 其次相应的开发人员可以被解雇或者重新部署到其他团队。
  4. Google Wave项目并没有从市场上取得成功,但是它的其中一些技术最终应用到了Google Apps上。比如:允许多个用户同时编写文档的功能就来源于Wave项目。
  5. 更常见的情况是核心服务经过多代并且老一代已被弃用。这种情况在谷歌经常发生。这种情况发生的如此之多,以至于看起来好像所有的Google内的服务都已经过时或者没有准备好。

建立服务

  1. 假如你是服务的所有者,你会如何里建立这种基于多语言聚合微服务的大型系统的服务?
  2. 一个在大型系统架构中表现良好的服务应该具有如下特征:
    • 目的单一性。这指的是简洁良好定义的接口。
    • 模块化和低耦合度。我们称之为微服务。
    • 不共享一个持久层。这点接下来我们会详细阐述。

服务所有者的目标是什么

  1. 满足你客户的需求。以适当的质量水平提供必要的功能,同时满足能够接受的性能水平,同时保持稳定性和可靠性,然后随时间不断去改进服务。
  2. 在满足客户需求的前提下最小化所需要的成本和工作量。
    • 这个目标和我们的激励方向一致,即鼓励最大化重用公共基础库。
    • 每个团队的资源总是有限的,所以团队也会对于如何重用已经久经测试的公共工具,流程,组件,服务很有兴趣。
    • 同时这也会激励团队采用更好的部署方式,比如自动化服务的构建和部署。
      0 总之这会激励人们去优化使用他们有限的资源
  3. 谁构建谁运行
    • 通常来说是某个小团队来负责具体服务从设计、开发、部署,直至彻底废弃。
    • 没有单独的维护工程师或者支持团队
    • 团队在服务范围内有权选择技术,方法和工作环境。
    • 团队为他们的选择负责。
  4. 服务的上下文是有限的,因为:
    • 团队的认知负荷时有限的
    • 我们也没有这样的需求,需要理解整个生态系统中的所有服务。
    • 团队只需要理解他自己们的服务,以及这些服务所依赖的服务而已。
    • 这就意味着团队可以非常小而灵活。一个典型的团队就3-5人。(就如美国海军陆战队的一个火力输出单元就4个人)
    • 小的团队规模也意味这沟通可以非常高效。
    • 基于康威定律“设计系统的组织,最终产生的设计等同于组织之内、之间的沟通结构”,因此小团队最终会导致小服务组件。
  5. 什么是服务之间的关系?
    • 推荐作为供应商的客户关系服务之间的关系,即使你们是在同一家公司。
    • 合作关系非常友好,但是联系关系结构非常清晰。
    • 所有权清晰。
    • 非常清楚谁在负责什么。在很大程度上,这取决于定义和维护清晰的接口。
    • 激励方向清晰。因为客户可以选择用或者不用你的服务。这就鼓励服务必须为客户着想,某些时候这也是服务被构建触发的原因。
    • 客户必须为服务付费。
    • 付费服务可以从经济上激励团队。这也鼓励了双方都会尽力保证资源的高效使用。
    • 免费服务会导致工作成果得不到认可,并且也没有热情去优化服务。
    • 比如:内部客户曾经可以免费使用Google Apps引擎,他们占用了大量的资源。祈求他们更高效的使用这些资源不是一个合适的策略。当一周后他们收到账单时,他们就立即采取措施降低了对于Google Apps引擎资源的占用多达90%以上。事实上这些措施仅仅是非常简单的一两个小改动。
    • 这并不是说Google Apps引擎使用客户是错误的,但是他们会有他们自己的优先级。因此,没有激励因素促使他们降低对于Google Apps引擎资源的占用。事实上,这最终导致了更高效的响应速度以及更高效的系统架构。
    • 收费策略也会促使服务提供者提供更高质量的服务。否则这些内部客户就会转向使用其他服务。这种激励直接导致了良好的开发和管理实践。代码审查就是其中一个案例,Google的庞大的构建和测试系统就是另一个。Google每天需要运行数以百万计的自动化测试。在每个改动被提交到仓库之前,验收测试都必须在所有的代码依赖路径上跑过。这有效的帮助了所有小团队都能保持他们服务的高质量。
    • 另外,付费模式也鼓励增量变化。小的变化更容易被理解。同时,代码变更的量和最终的产生的影响不具有线性关系。一个上千行的代码改动的风险不是十倍于一次一百行的改动,实际上要超过一百倍的风险。
  6. 维护接口的前后兼容性
    • 永远不要因为底层服务的变更而导致客户原有代码出错
    • 这意味这需要维护多个版本的接口。在一些麻烦的情况下,这意味着需要维护多个部署,一个最新的版本和其他多个老版本。
    • 通常来说接口不会改变,因为我们会使用小步快跑的增量模型去开发。
  7. 需要一个明确的废弃策略。服务提供者需要强烈推荐所有客户尽快迁移到某个版本或者这个版本之上。

规模化运营服务

  1. 作为一个基于大型多语言聚合微服务系统的服务提供者,在运营服务时的感受是什么?
  2. 性能的可预测性是基本需求
    • 任何服务在大规模系统中运行时的性能表现都可能大幅波动。
    • 性能的可预测性甚至比所谓的平均性能要重要的多。
    • 低延迟但是不稳定的性能表现,实际上完全不代表真正性能是低延时的。
    • 基于一个性能可靠稳定的服务,客户能够更加容易的编码实现。
    • 某个服务之中的高延时会导致大量使用该服务的其他服务产生高延时。
    • 假设一下,有一个服务的延时中位数是1毫秒,但是它有万分之一的机会延时是1秒。
    • 那么每次运行都意味着会额外损失0.01%的时间。
    • 假如你现在在同时跑5000台机器,就像很多如同Google这样的大型系统服务一样。那么你会额外损失50%的时间。
    • 再比如,某个百万份之一概率的内存缓存问题被发现,这会影响底层数据结构重新分配事务。这种罕见的问题暴露出来的表象就是在更高层次上的延迟大幅波动。像这种底层技术细节在大型系统架构中必须被非常重视。
  3. 高度弹性的容错机制
    • 服务中断更多时候是由于人为错误,而不是软硬件问题。
    • 服务需要能弹性容错,无论是机器,集群或者数据中心故障。
    • 在调用其他服务时一定要考虑到负载平衡和流量控制。
    • 需要能够快速回滚任意改动。
  4. 增量部署
    • 使用隔离系统。不要一次性部署所有的机器。选择某个系统部署你的新版本软件,然后观察它在这个新系统中的行为表现。
    • 假如工作已经进行到了大规模部署阶段。刚开始只升级10%机器,然后是20%,以此类推直到所有。
    • 假如在部署阶段中发现问题的话,你仍然能够选择回滚。
    • eBay曾经使用过一种部署机制,是基于功能标志位来分离新功能部署中的影响。典型来说,新功能会被默认部署为非激活状态,然后它可以被开启或者关闭。这样可以保证在新功能开启之前,这些代码就可以被正确部署。同时,假如新的功能有Bug,比如说性能问题或者商业上的考量,那么我们可以非常简便的关闭这个功能而不必再部署一次新代码。
  5. 我们需要尽可能多的提醒,而不是尽可能多的监控。

反模式法设计服务

  1. 超大服务
    • 不要在同一个服务做实现了太多功能。在这个生态系统中,你所需要的是简洁精简的服务。
    • 一个同时实现太多功能的服务只是一个障碍。让你难以理解,难以扩展,难以变更,同时也会产生远超你所需要的和其他服务太多的上下行依赖关系。
  2. 共享持久层
    • 在分层模型中,服务往往处于应用层,而持久层是作为一个公共服务提供给所有应用程序。
    • eBay曾经尝试过,并证明这个方法不可行。即尝试打破服务封装,应用程序可以通过后门直接更新数据库。最终他们终止这个行为,重新改写了若干服务。共享数据库不再允许松耦合服务。
    • 微服务通过小型化,隔离化,独立化来防止问题产生。这也是如何是你的生态系统保持健康和成长的重点。

相关文章:

原文链接:Deep Lessons From Google And EBay On Building Ecosystems Of Microservices(译者/王旭敏 审校/朱正贵 责编/仲浩)
译者介绍:王旭敏,Nokia开发工程师,关注云计算、高性能或可用等架构、容器等。

Comments are closed.