最近,围绕着面向服务架构,尤其是微服务架构的弊端,业界议论纷纷。几年前,由于许多用户关注微服务架构的众多优势,例如独立部署形式的灵活性、清晰的所有权、提高的系统稳定性和更好的关注点分离,许多企业很快采用了微服务架构。现在,微服务会大大增加其复杂性,有时甚至连微不足道的功能都难以构建的趋势引发了许多用户的讨论。Uber近年来一直在使用微服务,现在Uber已经发展到大约2200个关键微服务。在这个过程中,优步做了很多取舍。Uber表示,在过去的两年里,他们一直在努力降低微服务的复杂性,同时仍然保持微服务架构的优势。这篇文章详细介绍了优步微服务架构的一般方法,优步称之为“面向领域的微服务架构”(简称:DOMA)。面对微服务的缺点,批判微服务架构的话题甚嚣尘上,但很少有用户主张完全拒绝微服务架构。由于运营收益非常重要,因此似乎有更好的微服务替代方案。Uber使用DOMA通用方法的目标是为企业提供一条微服务路径,以降低整体系统复杂性,同时保持与微服务架构相关的灵活性。什么是微服务?微服务是面向服务架构的扩展。微服务代表具有一组狭窄功能的应用程序,与过去的大型单体“服务”相反。这些应用程序在Web上托管和可用,并公开定义明确的接口。其他应用程序通过进行“远程过程调用”(RPC)来调用此接口。微服务架构的一个关键特征是代码的托管、调用和部署方式。如果我们考虑大型单体应用程序,它们通常会被划分为具有定义良好接口的封装组件。这些接口将直接称为进程内接口,而不是通过网络。通过这种方式,我们可以开始将微服务视为具有性能问题(网络I/O和序列化/反序列化)的库,以便调用它的任何功能。当我们以这种方式思考微服务时,我们可能会想我们为什么要采用微服务架构?答案通常是独立部署和扩展。对于大型单体应用程序,企业必须一次部署或发布所有代码。应用程序的每个新版本都可能涉及许多更改,部署变得有风险且耗时。任何错误都可能导致整个系统崩溃。换句话说,企业采用微服务以牺牲性能为代价来获得运营价值。企业还必须承担维护支持微服务所需的基础设施的成本。事实证明,这种权衡在很多情况下都是有意义的,但这是反对过早采用微服务架构的论据之一。Uber采用微服务架构的由来,是因为在2012-2013年左右,Uber有两个单体服务,使用微服务解决了很多运维问题。可用性风险。单个代码库中的单个回归可能会导致整个系统崩溃。有风险的部署。由于经常需要回滚,执行这些操作既痛苦又耗时。关注点分离不佳。对于庞大的代码库,很难保持良好的关注点分离。在指数增长的环境中,权宜之计有时会导致逻辑和组件之间的界限不明确。执行效率低下。这些问题共同导致团队难以自主或独立执行。换句话说,随着Uber从10名工程师发展到100名工程师,拥有多个团队,具有部分技术堆栈的整体架构将团队的命运捆绑在一起,使其难以独立运营。Uber采用微服务架构后。最终,系统变得更加灵活,让团队更加自主。系统可靠性。在微服务架构中,整体系统的可靠性得到了提高。可以在不关闭整个系统的情况下关闭(和回滚)单个服务。关注点分离。在架构上,微服务架构迫使Uber提出一个问题:“为什么这个服务存在?”,从而更清晰地定义不同组件的角色。清晰的所有权。谁拥有什么代码变得更加清晰。服务通常由个人、团队或企业级别拥有,从而实现更快的增长。自主执行。单独的部署和更清晰的所有权线使单个产品和平台团队能够自主执行。开发速度。团队可以独立部署代码,这使他们能够按照自己的节奏执行。可以毫不夸张地说,如果没有微服务架构,Uber就无法达到今天的规模和执行质量。然而,随着Uber规模的扩大,从100名工程师增加到1000名工程师,Uber开始注意到一系列与系统复杂性大大增加相关的问题。使用微服务架构,可以将单一的单体代码库替换为一个“黑盒子”,其功能随时可能发生变化,很容易引发事故。为了调试取件问题,工程师必须在12个不同的团队中完成大约50项服务。了解服务之间的依赖关系可能会变得困难,因为服务之间的调用可能深入许多层。第n个依赖项中的延迟峰值可能会导致级联的上游问题。如果没有合适的工具,就不可能看到实际发生了什么,从而使调试变得困难。Jaeger于2018年年中发布的Uber微服务架构为了构建简单的功能,工程师通常需要跨多个服务工作,而这些服务由不同的个人和团队拥有。这需要在会议、设计和代码审查中花费大量协作和时间。随着团队在彼此的服务中构建代码、修改彼此的数据模型,甚至代表服务所有者执行部署,先前对服务所有权的明确承诺受到损害。可以形成一个网络单体,其中所有看似独立的服务必须一起部署以安全地执行任何更改。大约2018年Uber的复杂流程示例,在DOMA之前需要10个接触点以进行简单集成。结果是开发人员体验变慢、服务所有者不稳定、迁移更痛苦等等。对于已经采用微服务架构的企业来说,已经没有回头路了。需要找到解决方案来克服这些挑战面向“领域”的微服务架构如果您可以将微服务视为I/O绑定库,将“微服务架构”视为大型分布式应用程序,则可以使用众所周知的架构来想想如何组织代码。因此,“面向领域的微服务架构”在很大程度上借鉴了既定的代码组织方式,例如领域驱动设计、简洁架构、面向服务的架构以及面向对象和面向接口的设计模式。Uber将DOMA视为一项创新,因为它是一种相对新颖的方法,可以在大型组织的大型分布式系统中利用既定设计原则。与DOMA相关的核心原则和术语如下:Uber不是围绕单个微服务,而是围绕相关微服务的集合——称为域。Uber通过创建称为层的域集合更进一步。域所属的层决定了允许该域内的微服务承担哪些依赖关系——称为层设计。Uber为域提供了一个干净的接口,这些域被视为集合的单一入口点——称为网关。最后,Uber确定每个域都应该与其他域不可知,也就是说,一个域不应在其代码库或数据模型中硬编码与另一个域相关的逻辑。由于团队通常需要在另一个团队的域中包含逻辑(例如,自定义验证逻辑或数据模型上的某些元上下文),我们提供了一个扩展架构来支持该域点中定义明确的扩展。换句话说,通过提供系统的架构、域网关和预定义的扩展点,DOMA打算将微服务骨架从复杂的东西转变为易于理解的东西:一组结构化的灵活、可重用和分层的组件。下面深入探讨Uber对DOMA的实施、它所看到的价值,以及对可能希望采用这种方法的企业的实用建议。DOMA的实施域Uber域表示绑定到逻辑功能分组的一个或多个微服务的集合。设计域时的一个常见问题是“域应该有多大?”优步在这里不提供指导。一些域可以包含几十个服务,而一些域可以只包含一个服务。重要的任务是仔细考虑每个集合的逻辑作用。比如Uber的地图搜索服务构成一个域,票价服务是一个域,匹配平台(匹配司机)也是一个域。这些也不总是遵循企业的组织结构。UberMaps组织本身分为三个域,在三个不同的网关后面有80个微服务。层设计层设计回答了“哪些服务可以调用其他哪些服务?”的问题。在Uber的微服务架构中,层设计可以被认为是“关注点的比例分离”。或者,层设计可以被认为是“大规模的依赖管理”。层设计描述了一种机制,用于考虑Uber的故障失效面(原文为failureblastradius)和跨服务依赖的产品特异性。随着域从底层移动到顶层,它们在发生中断时影响的服务更少,并且代表更具体的产品用例。相反,底层功能具有更多依赖性,因此往往具有更大的故障面并代表一组更通用的业务功能。下图说明了这个概念。将顶层视为特定的用户体验(例如移动功能),将底层视为一般业务功能(例如帐户管理)。这为我们思考断层面、领域整合等问题提供了有益的启发。值得注意的是,在此图中,功能通常从特定的“下”到更一般的“下”。可以想象,随着需求的发展,一个简单的功能最终会变得越来越像一个平台。事实上,这种向下迁移是意料之中的,Uber的许多核心业务平台最初都是为乘客或司机提供特定功能,随着我们开发更多的业务线,它们变得越来越通用(比如UberEats或UberFreight)。在优步内部,建立了以下五个层次。基础设施层。提供任何工程团队都可以使用的功能。这是优步对存储或网络等重大工程问题的回答。业务层。提供优步作为一个组织可以使用的功能,但这些功能并不特定于特定的产品类别或业务线(LOB),例如乘车、餐饮或货运。产品层。提供与特定产品类别或LOB相关但与移动应用程序无关的功能,例如多个叫车应用程序(Rider、Rider“Lite”、m.uber)使用的“请求乘车”逻辑.com等)。表示层。提供与面向消费者的应用程序(移动/网络)中存在的特性直接相关的功能。边缘层。安全地对外开放优步服务。该层还支持移动应用程序。如您所见,每个后续层代表越来越具体的功能分组,并且故障面越来越小(换句话说,依赖于该层内功能的组件越来越少)。网关术语“网关API”已经是微服务架构中广泛建立的概念。Uber的定义与既定的定义几乎没有区别,只是它倾向于将网关专门视为一组称为域的基础服务的单一入口点。网关的成功取决于API设计的成功与否。上图说明了网关的高级图。它抽象出域的内部细节——多个服务、数据表、ETL管道等。只有接口——RPCAPI、消息传递事件和查询暴露给其他域。由于上游消费者只对单个服务进行操作,通过上游服务只接受单个依赖(而不是依赖于一个域中可能存在的多个下游服务),网关在未来的迁移、可发现性和系统中有很多在整体降低性别方面的好处。如果您从面向对象的设计角度考虑网关,它们就是接口定义,使Uber能够对底层“实现”(在本例中为底层微服务的集合)做任何它想做的事。扩展扩展表示扩展域的机制。扩展的基本定义是它提供了一种机制来扩展底层服务的功能,而不改变该服务的实际实现或影响其整体可靠性。Uber提供了两种不同的扩展模型:逻辑扩展和数据扩展。扩展的概念允许Uber将架构扩展到多个团队,能够彼此独立工作。逻辑扩展逻辑扩展提供了一种用于扩展服务底层逻辑的机制。对于逻辑扩展,优步使用提供者或插件模式的变体,并在逐个服务的基础上定义接口。这使得扩展团队能够以接口驱动的方式实现扩展逻辑,而无需修改底层平台的核心代码。例如,司机上线。通常,我们会进行各种检查以确保驱动程序正常运行(安全检查、合规性等)。每个人都属于一个单独的团队。实现此目的的一种方法是让每个团队在同一端点编写逻辑,但这会带来复杂性。每项检查都需要自定义的、完全不相关的逻辑。对于逻辑扩展,“上线”端点将定义一个接口,每个扩展都应符合预定义的请求类型和响应。每个团队将注册一个负责执行此逻辑的扩展。在这种情况下,他们可能只获取有关驱动程序的一些上下文并返回一个布尔值,说明驱动程序是否准备好上线。在线端点将简单地遍历这些响应并确定它们中是否有任何错误。这将核心代码与每个扩展分离,并在不知道其他逻辑正在执行的情况下提供扩展之间的隔离。围绕它构建更多功能很容易,例如可观察性或功能标志。数据扩展数据扩展提供了将任意数据附加到接口的机制,以避免核心平台数据模型膨胀。对于数据扩展,Uber利用了Protobuf的Any特性,该特性允许团队向请求添加任意数据。服务通常存储此数据或将其传递给逻辑扩展,以便核心平台永远不会反序列化(并因此“知道”)此任意上下文。Protobuf的Any实现带来了一些基础设施开销,以换取更强的类型。对于更简单的实现,JSON字符串可以简单地用于表示任意数据。定制除了逻辑和数据扩展之外,Uber的许多团队还引入了适合其领域的扩展模式。例如,许多与表示架构相关的集成使用基于DAG的任务执行逻辑。价值所在Uber的几乎每个主要领域都以某种方式受到DOMA的影响。在过去的一年里,Uber主要专注于业务层,为不同的业务线提供通用的逻辑。DOMA在优步还很年轻,但它在简化开发人员体验和降低整体系统复杂性方面的早期表现非常积极。产品和平台DOMA是优步产品和平台团队共同努力的结果。平台支持成本通常会下降一个数量级。产品团队受益于护栏和加速开发。例如,我们扩展架构的早期平台采用者能够通过采用扩展架构缩短代码审查、规划和用户学习曲线的时间,将确定新功能优先级和集成新功能的时间从三天缩短到三小时。降低复杂性以前的产品团队不得不调用大量下游服务来利用一个域;现在他们只需要打电话给一个。通过减少加载新功能的接触点数量,平台能够将入职时间减少25-50%。此外,2200个微服务可以划分为70个域。其中大约50%已经实施,并且大多数都有未来采用的计划。FutureMigration在Uber,微服务的半衰期计算为1.5年,这意味着每1.5年我们的微服务将有50%的变化。没有网关,微服务架构很容易陷入“迁移噩梦”。改变微服务需要不断的上游迁移。网关使团队能够避免对底层域服务的依赖,这意味着这些服务可以在不强制上游迁移的情况下进行更改。这些平台有数百种服务依赖于它们,而这些服务将不得不迁移现有的消费者。在这些情况下,迁移成本可能高得令人望而却步,因此重写整个平台变得不可行。新的业务和产品线证明,使用DOMA设计的平台具有更高的可扩展性和易维护性。Uber的大多数采用DOMA的团队之所以这样做,是因为支持新业务线的成本太高。实用建议Uber为希望采用DOMA的公司提供了一些实用建议。指导原则是,根据Uber的经验,成熟且经过深思熟虑的微服务架构来自于在正确的时间向正确的方向缓慢移动。现实情况是,对整个微服务架构进行真正的“重写”是不可能的。因此,优步认为,微服务架构的发展更像是“修剪园艺”,一步步正确生长,而不是自上而下或一次性架构(或重新架构)。这是一个动态的、渐进的过程。创业驱动的问题应该是“什么时候应该采用微服务架构?”和“这对企业有意义吗?”代价是增加了复杂性,这使得构建功能更加困难。在初创企业中,运营收益可能无法抵消增加的架构复杂性。此外,微服务架构通常需要专门的工程资源来支持,这对于早期公司来说可能超出预算,或者从优先级的角度来看不是一个好的选择。考虑到这一点,将微服务完全搁置一段时间并不是没有道理的。如果企业确实选择采用微服务,则应该考虑“将微服务视为大型分布式应用程序”的类比,并考虑要构建的微服务之间的关注点分离。另外请注意,第一个微服务可能真正描述了业务的核心,因此可能是最重要的。不同功能和平台之间的微服务架构对于已经拥有成熟团队并且关注点之间的明显区别变得模糊的中型企业变得更加有用。这个阶段,企业可以开始思考微服务之间的层次结构。随着某些服务对业务运营变得越来越重要并且越来越多的团队依赖于它们,依赖性管理可能会变得更加重要。对平台化的早期投资可能会在未来得到回报。如果可以创建一个完全与产品无关的业务平台,并避免核心平台服务中的任意产品逻辑,那么就有可能避免技术债务。在这一点上,使用扩展来实现这个目标是有意义的。鉴于微服务的数量可能仍然很低,将它们组合在一起可能没有意义。然而,这里值得注意的是,Uber的DOMA实现上下文中的域可以包含单个服务,因此以“面向域”的方式思考仍然有用。较大的企业组织可能有数百名工程师和具有多个依赖项的微服务。此时,DOMA已完全发挥作用。可能有明显的微服务集群,可以通过放置在它们前面的网关轻松组合在一起。遗留服务通常需要先重构或重写,然后再迁移。这意味着如果网关已经存在,该网关将很快开始提供易于迁移的价值。清晰的层次结构也将变得越来越重要,因为一些服务作为特定功能或功能组的“产品”服务运行,而其他服务将越来越多地支持多种产品并被视为“平台”服务。”。在这个阶段,将任何产品逻辑与平台保持分离是至关重要的,以避免平台团队的沉重运营负担和整个系统的不稳定。正如最后所写,优步越来越多的团队已经采用DOMA并且仍在积极开发DOMA。DOMA的关键见解是微服务架构实际上只是一个大型分布式程序,是一种可以应用于所有软件的原则和演进。DOMA只是在实践中考虑这些原则的一种方式。
