介绍
最近,已经积极地讨论了面向服务的体系结构,尤其是微服务体系结构(MA)的缺点。就在几年前,许多人都愿意迁移到MA,因为它具有许多优势:独立部署形式的灵活性,透明的所有权,增强的系统稳定性以及更好的关注点分离。但是,情况最近发生了变化:微服务方法因其严重增加复杂性的趋势而开始受到批评,这有时甚至难以实现琐碎的功能。(我们在“微服务:大小很重要,即使您拥有Kubernetes ”中也谈到了这一点-大约翻译。)
Uber目前拥有约2,200个关键微服务,而我们自己已经体验了这种方法的所有利弊。在过去的两年中,Uber试图降低微服务格局的复杂性,同时保持架构的优势。在这篇文章中,我们计划介绍我们的微服务架构通用方法,称为面向领域的微服务架构(DOMA)。
尽管近年来批评微服务架构的缺点已广为流行,但很少有人敢宣称它应该被完全抛弃。他们的经营利益太重要了;此外,这种方法似乎没有(或极其有限)的替代方案。我们通用方法的目标是帮助希望降低总体系统复杂性同时保持MA灵活性的组织。
本文将探讨DOMA,在Uber中导致此方法的挑战,其对平台和产品团队的好处,最后为那些希望迁移到此体系结构的人提供一些技巧。
什么是微服务?
微服务是面向服务的体系结构的扩展。与2000年代相当大的“服务”不同,微服务执行特定的任务。这些应用程序可以通过网络托管和访问,并提供定义良好的界面。其他应用程序使用远程过程调用(RPC)访问此接口。
MA的关键特征是代码的发布,调用和部署方式。大型的整体应用程序通常分为具有明确定义的接口的封装组件。然后直接从过程内部而不是通过网络调用这些接口。从这个意义上讲,微服务在调用任何函数时都可以被认为是一种性能较低的库(由于网络延迟和时间对序列化/反序列化的影响)。
以这种方式考虑微服务,我们可能想知道为什么我们根本需要微服务架构?这个问题的经典答案是因为能够独立部署单个组件并轻松扩展它们。... 对于大型的整体应用程序,组织被迫同时部署或发布所有代码。结果,每个新版本都会进行很多更改。部署变得既危险又费时。任何错误都可能导致整个系统瘫痪。
因此,公司正在向微服务为便于使用而牺牲性能。他们还必须承担维护微服务所需基础架构的额外费用。经验表明,在许多情况下,这种折衷是有道理的。同时,这是反对过早过渡到MA的有力论据。
动机
在向微服务过渡时(大约2012-2013年),我们在Uber拥有两个主要的整体服务,我们面临着许多微服务成功解决的运营问题:
- 可用性风险。整体代码库中的任何错误都可能导致整个系统(在本例中为整个Uber)掉线。
- 风险高昂的部署。它们很难执行,并且常常不得不回滚到以前的版本。
- 责任区划分不当。在庞大的代码库中,很难跟踪谁对什么负责。随着指数级增长,匆忙有时会模糊逻辑和组件之间的界线。
- 工作效率低下。上述问题共同导致团队难以独立或彼此独立地工作。
换句话说,在Uber的工程师人数从几十人增加到数百人以及拥有自己属于技术栈的大量团队的出现的背景下,整体式架构越来越束缚了这些团队的命运,不允许他们独立工作。
因此,我们决定切换到MA。结果,我们的系统变得更加灵活,并使团队变得更加自治。
- 系统可靠性。随着向MA过渡,整个系统的可靠性也随之提高。单个服务可以崩溃(并且可以回滚到以前的版本),而不会冒着崩溃整个系统的风险。
- . - : « ?», — .
- . , . , , , , .
- . .
- . .
毫不夸张地说,没有MA,Uber不可能达到目前的规模和质量水平。
但是,随着公司的持续发展和工程师人数从数百增加到数千,我们开始注意到与系统复杂性显着增加相关的许多问题。在MA的情况下,我们牺牲了一个整体代码库,以换取许多“黑匣子”,这些黑匣子的功能可以随时更改,并导致意外行为。
例如,工程师必须分析12个不同团队中的〜50个服务才能找到问题的根源。
了解服务之间的依赖关系可能会变得非常困难,因为它们可以在许多级别相互交互。第n个依存关系的延迟峰值可能会导致上游服务出现大量问题。此外,如果没有适当的工具,将无法理解发生了什么。所有这些使调试非常困难。
Jaeger截至2018年中的Uber微服务架构
为了实现最简单的功能,工程师通常必须使用许多服务,而完全由不同的团队和人员负责。结果,花了很多时间来组织团队合作,会议,设计咨询和代码审查(核心审查)。随着团队不断入侵彼此的服务,更改数据模型甚至代表服务所有者进行部署,所有权透明化的最初好处逐渐变得模糊。这会创建网络整体,其中服务似乎只是独立的,但实际上必须将它们一起部署才能安全地进行任何更改。
Uber(〜2018)中如此复杂的系统的示例,它具有十个接触点,易于集成(甚至在DOMA之前)。
结果,我们的开发流程变慢,服务所有者的不稳定,迁移过程更加耗时,等等。las,对于那些已经转向MA的组织来说,没有回头路可走。众所周知的短语完美地说明了这种情况:“不可能与他们生活在一起,也不能射击他们。”
特定领域的微服务架构
将微服务视为I / O链接库,将微服务体系结构视为庞大的分布式应用程序。在这种情况下,我们可以使用著名的体系结构解决方案来考虑如何最好地组织我们的代码。
因此,面向域的微服务体系结构(DOMA)可以依靠完善的组织代码的方式,例如面向域的设计,干净的体系结构,面向服务的体系结构以及面向对象和面向接口的开发模式。我们认为DOMA是创新的,因为它是在大型组织的全球分布式系统中利用现有设计原则的相对较新的方法。
以下是一些DOMA基本概念和相关术语:
- 与其查看单个微服务,不如查看它们的组。我们称它们为domains (域)。
- 接下来,我们结合所谓的各层 (各层)的域。域所属的层确定该域中的微服务可以使用哪些依赖项。我们称这种多层的 架构为(多层设计)。
- , . (gateways).
- , , , 'hardcode' , . (, - ), (extension architecture) .
换句话说,结构化体系结构,域网关和预建的DOMA可扩展性点将微服务体系结构从复杂的事物转变为可理解和有形的事物:结构化的一组灵活,可重用和分层的组件。
本文的其余部分将重点介绍Uber对DOMA的实施及其优势。还将为希望采用这种方法的公司提供实用建议。
在Uber中实施
域
Uber域是一个或多个微服务的集合,这些服务基于功能的逻辑组合链接在一起。问题自然产生了,该域应该有多大。在这种情况下,我们不提供任何指示。一些域可以包含数十种服务,而其他域仅仅是一项。在这里重要的是要仔细考虑每个关联的逻辑角色。例如,我们将地图上的搜索服务,票价服务,选择服务(比较驾驶员和乘客)分组到单独的域中。此外,他们并不总是重复公司的组织结构。Uber Maps分为三个域,其中80个微服务隐藏在三个不同的网关后面。
基于层的架构
多层体系结构回答了在MA Uber的范围内可以进行哪种服务和哪种通信的问题。也就是说,可以将其视为责任区域的全球分布或全球依赖管理的机制。
分层体系结构有助于了解故障后损坏的半径,并根据从属服务Uber的数量反映产品的特殊性。当您从底部移至顶部时,发生故障时受影响的服务数量将减少,并且产品的应用范围将缩小。反之亦然,更多的服务取决于较低级别的功能,因此,通常,由于故障而造成的损害半径更大,并且要解决的业务任务范围也更广。下图说明了此概念。
您可以想象,较高的级别专注于负责特定(狭窄)用户体验的功能(例如,移动功能),而较低的功能则由更多的全球业务功能(例如,帐户管理或乘车共享市场旅行)所占据...每层仅取决于底层,这使诸如爆炸半径和域集成之类的概念更加清晰。
值得注意的是,功能在此图中经常向下移动,从窄到宽。您可以想象一些简单的功能会随着需求的发展而变得越来越重要(“平台”)。实际上,这种向下迁移是可以预料的,而且Uber的许多核心业务平台最初都是为驾驶员或乘客提供的功能,随着时间的推移,它已经发展并越来越普遍地作为新的业务领域(例如Uber Eats或Uber Freight ),并将更多依赖项连接到它们。
在Uber中,我们将以下五个级别进行了区分。
- . , . — Uber , .
- -. , Uber , , Rides (), Eats ( ) Freight ( ).
- . , , . , «request a ride» ( ) , Rides: Rider, Rider «Lite», m.uber.com, ..
- . , (/), .
- . Uber . .
如您所见,每个后续级别代表的功能组合越来越狭窄,命中半径也更小(换句话说,更少的组件依赖于此层中的功能)。
网关
API网关一词已经在微服务架构中很好地确立了。我们的定义与公认的定义没有太大不同-除了我们倾向于将网关视为相应服务组(我们称为域)的单个入口点。网关的成功取决于设计良好的API架构:此图说明了网关的高级设计。它从域内部结构的细节中抽象出来:一组服务,带有数据的表,ETL管道等。其他域只能访问以下接口:用于消息系统中远程过程调用,事件和请求的API。
由于上游使用者仅运行一项服务,因此,当上游服务仅具有一种依赖性(而不是依赖于多个下游服务)时,网关在将来的迁移,可发现性以及系统复杂性的总体降低方面提供了许多好处。可能存在于域中)。从OO设计的角度来看,网关是接口定义,它使我们可以使用内部“实现”(即一组微服务)来完成我们想要的任何事情。
扩展名
顾名思义, 扩展名(extensions)是一种扩展域的机制。这种附加组件的基本定义是,它提供了一种扩展服务功能的机制,而无需更改该服务的内部结构或影响其整体可靠性。在我们的Uber中,有两个扩展模型:逻辑 (逻辑扩展)和基于数据 (数据扩展)。扩展概念使我们能够扩展架构,以便多个团队可以彼此独立地工作。
逻辑扩展
逻辑扩展提供了一种扩展服务基础逻辑的机制。对于他们,我们使用一种提供程序或插件模式,以及为每个服务分别定义的接口。这允许团队仅使用界面来实现其逻辑,而不会干扰主要平台代码。
例如,假设驱动程序在线。我们通常会进行各种检查,以确保其具有联机状态(出于安全性,合规性等)。他们每个人都有自己的团队。一种可行的方法是强制每个命令在同一端点写入逻辑,但这会增加复杂性。每次检查都将需要不同且完全不相关的逻辑。
如果逻辑端点扩展称为联机将定义每个扩展应符合预定义的请求和响应类型的接口。每个团队都将注册一个扩展,负责实现此逻辑。在这种情况下,他们可以简单地获取有关驱动程序的一些信息并返回一个逻辑值(布尔),该逻辑值将确定驱动程序是否“值得”在线状态。端点本身(在线)将简单地遍历这些答案并确定它们是否为假。
这种方法将核心代码与扩展分开,并在它们之间提供隔离。但是,扩展不知道正在执行什么其他逻辑。这样可以轻松创建其他功能,例如可观察性或特征标记。
数据驱动的扩展
这种扩展提供了一种机制,用于将任意数据附加到接口,以避免不必要地破坏底层平台的数据模型。在数据扩展,我们积极利用的功能,如任何来自的Protobuf,它允许将任意的数据请求。服务通常会存储此数据或将其传递给逻辑扩展,以便主平台永远不会反序列化(因此不会“知道”任何信息)有关此任意上下文的信息。任何实现都会产生一些基础结构开销,以换取更强的键入。一个更简单的替代方法是JSON格式来表示任何数据:
任意补码
除了布尔和数据扩展,Uber的许多团队还开发了自定义扩展模板以匹配其域。例如,与表示架构有关的大多数集成都使用基于DAG的任务执行逻辑。
好处
DOMA对Uber几乎所有主要业务都产生了不同程度的影响。在过去的一年中,我们主要专注于业务层。它为公司的各种业务提供通用逻辑。
对于Uber而言,DOMA相对来说还比较陌生,将来,我们肯定会分享更多信息和我们的架构示例。最初的结果令人鼓舞:它们极大地简化了开发人员的工作,并降低了系统的整体复杂性。
产品和平台
DOMA是Uber的各个产品和平台团队之间的共同努力。在许多情况下,平台支持成本下降了一个数量级。产品团队受益于特异性和加速的开发。
例如,我们扩展架构的早期平台使用者能够通过减少代码审查时间,调度和加速使用者培训,将优先级和集成新功能的时间从三天缩短为三小时。
降低复杂度
以前,产品团队必须使用一个域中的许多下游服务,但是现在他们只需要调用一个即可。通过在引入新功能时减少接触点的数量,实现时间减少了25-30%。此外,我们能够在70个域中分发2,200个服务。其中约有一半已经实施,对于大多数,有一项计划以一种或另一种形式实施。
未来的迁移
在Uber,我们计算出微服务的半衰期为1.5年。换句话说,我们每年50%的服务都失去了相关性。没有网关,微服务架构可能会变成迁移地狱。不断变化的微服务需要持续的上游迁移。网关使团队可以避免依赖于下游域服务,这意味着这些服务可以更改而无需迁移到上游。
过去一年中,Uber最大的两次平台升级发生在网关后面。这些平台具有数百个相关服务,并且如果没有网关,则必须迁移所有现有的使用者。这将是非常昂贵的,这将使平台的完整重新设计变得不现实。
新的业务和产品线
基于DOMA的框架已被证明具有更高的可扩展性和易于维护性。Uber切换到DOMA的大多数团队之所以这样做,是因为维护新业务线变得太昂贵了。
实用建议
在本节中,我为可能对DOMA感兴趣的公司汇编了一些实用技巧。这里的指导原则是,根据我们的经验,成熟而周到的微服务架构基于在正确的时间在正确的方向上的增量转移。实际上,完全“重写” MA几乎是不可能的。
因此,我们将MA的演进视为一种“削减对冲”的过程,这要归功于它朝着正确的方向发展,而不是一次性的,自愿的努力。这是一个动态的,渐进的过程。
初创企业
这里的关键问题是:“我们什么时候应该搬到MA?”和“这对我们的组织有意义吗?”如上所述,尽管微服务在拥有大量工程师的组织中提供了运营优势,但它们也增加了整体复杂性,使实施新功能变得困难。
在小型组织中,运营优势不太可能弥补增加的体系结构复杂性。而且,MA通常需要专用的工程资源来支持它们,这对于早期公司而言可能太昂贵,或者在优先级方面根本不是最优的。
话虽如此,将过渡到微服务的时间推迟一会儿是明智的。如果组织决定切换到微服务,我们建议它使用大型分布式应用程序的类比,并事先考虑在服务之间划分问题区域。还请记住,最早的微服务可能是最重要且寿命最长的服务,因为它们描述了业务的关键部分。
中型企业
在拥有许多团队的中型公司中,MA的效用有所提高,在这些公司中,职责范围在不同职能和平台之间逐渐模糊。
在这里,您可以开始考虑微服务的层次结构。依赖管理可能会变得越来越重要,因为某些服务对于运营企业变得越来越重要,而更多的团队将依赖它们。
对平台的早期投资可以在以后派发红利。创建不依赖于其他产品的业务平台可以避免技术债务的积累,也可以避免将任意产品逻辑渗透到平台的主要服务中。也许在这个阶段应该引入扩展机制来实现这个目标。
鉴于微服务的数量仍然很少,因此将它们捆绑在一起可能没有任何意义。但是,在这里值得注意的是,在Uber中实现DOMA的上下文中的一个域很可能包含单个服务,因此“面向域”的思路仍然不会受到损害。
大生意
大型工程组织可以拥有数百名专家,微服务和许多依赖项。在这些条件下,DOMA发挥了全部潜力。这样的公司肯定会拥有明显的微服务集群,这些集群可以很容易地合并到前面带有网关的域中。旧版服务通常需要重构/重写和后续迁移。这意味着网关将很快开始带来易于迁移方面的实际好处(如果它们已经被部署的话)。
透明和可理解的层次结构的重要性也将提高:某些服务将成为某些功能或功能组的“产品”,而其他服务将支持多个产品并充当“平台”。在此阶段,至关重要的是将任意产品逻辑与平台分开,以避免对平台团队造成巨大的运营压力,并最大程度地降低全局系统不稳定的风险。
最后的想法
在Uber,随着更多团队迁移到DOMA,我们将继续积极开发DOMA。 DOMA背后的主要思想是微服务架构只是一个大型的分布式程序。并且可以将相同的原理应用于任何其他软件的演变。 DOMA只是对这些原理进行实际思考的一种方法。希望对您有所帮助,并期待您的反馈!
DOMA本身是来自Uber所有部门的近60名工程师跨职能工作的结果。我要特别感谢以下人员在过去两年中对这项工作的贡献:
Alex Zylman,Alexandre Wilhelm,Allen Lu,Ankit Srivastava,Anthony Tran,Anupam Dikshit,Anurag Biyani,Daniel Wolf,Deepti Chedda,Dmitriy Bryndin,Gaurav Tungatkar,Jacob Greenleaf,Jaikumar Ganesh,Jennie Ngyabuae,Joeoshin ,库沙·卡普尔(Kusha Kapoor),琳达·富(Linda Fu),玛丹·唐卡鲁(Mdan Thangavelu),尼米什·谢斯(Nimish Sheth),帕斯·沙(Shah Burke),西蒙·牛顿(Simon Newton),史蒂夫·舍伍德(Steve Sherwood),乌代·基兰·梅迪塞蒂(Uday Kiran Medisetty)和瓦尔·卡杜斯(Waleed Kadous)。
致谢:这项工作结合了行业中许多现有的设计模式来解决Uber中的问题,并且还提出了一些新的模式(例如扩展)。我们感谢业界为他们而努力。我们也感谢在Superblocks上与我们分享经验的Linkedin工程师。
译者的PS
另请参阅我们的博客:
- «: , Kubernetes»;
- « 2018 ».