前言
近来,一些关于面向服务架构的话题,特别是针对微服务架构的弊端这个话题上进行了大量的讨论。虽然在几年前,微服务架构受到不少人的青睐,由于它们提供了许多好处,如独立部署的灵活性、明确的全部权、系统稳定性的改善以及更好的分离问题等优势。可是不久,就开始有人吐槽微服务会大幅增长系统复杂性,有时甚至连一些简单的功能都难以构建。设计模式
随着Uber发展,咱们目前拥有了大约2200个关键的微服务,而且也亲身经历了这些权衡。在过去的两年里,Uber一直在试图下降微服务的复杂性的同时仍然保持着微服务架构的优点。咱们但愿经过这篇博文介绍咱们对微服务架构的通用方法,咱们称之为 "面向领域的微服务架构"(DOMA)。安全
因为这些缺点,近年来也有一些批评微服务架构的声音,可是却不多有人主张完全拒绝微服务架构。运营收益过重要了,并且彷佛没有有效的替代方案。咱们介绍DOMA的目的是为了给那些但愿下降总体系统复杂性,同时又保持微服务架构相关灵活性的组织提供一些经验建议。微信
这篇文章主要解释了什么是DOMA,以及Uber采用这种架构的缘由,它对平台和产品团队带来哪些好处。最后,给想要采用这种架构的团队一些建议。网络
什么是微服务
微服务是面向服务架构的延伸。与2000年代至关大的 "服务 "相比,微服务是表明一组小型功能的应用程序。这些应用经过网络托管,并暴露出一个明肯定义接口。其余应用程序经过进行远程过程调用(RPC)方式来调用这个接口。架构
微服务架构的特色是代码的托管、调用和部署方式。好比大型的单体应用,它们一般会被分割成具备明肯定义接口的封装组件。而后,这些接口就能够直接在进程中调用,而不是经过网络。经过这种方法,咱们将微服务看做一个性能受到影响的库(网络I/O和序列化/反序列化),以便调用它的任何功能。分布式
当咱们以这种方式来思考微服务时,可能会质疑为何咱们会采用微服务架构。答案一般是独立部署和扩展。对于一个大型的单体应用程序,应用被迫一次性部署或发布全部的代码。应用程序的每个新版本均可能涉及许多更改。部署变得风险大、耗时长。任何人均可以使整个系统瘫痪。ide
换句话说,业务采用微服务是以牺牲性能为代价来获取运营利益。业务还必须承担维护支持微服务所需的基础设施的成本。事实证实,在不少状况下,这种权衡是颇有意义的,但这也是反对过早采用微服务架构的有力论据。微服务
动机
在Uber,咱们也采用了微服务架构,由于咱们(大约在2012-2013年)主要有两个单体服务,遇到了不少经过微服务来解决的运营问题。工具
可用性风险。单体代码库内的一次回滚就会使整个系统(在本例中是Uber的所有)瘫痪。性能
部署风险大,成本高。在须要频繁回滚的状况下,执行这些操做既困难又耗时。
不平滑的关注点分离。在庞大的代码库中,很难保持良好的关注点分离。尤为在一个指数级增加的环境中,权宜之计有时会致使逻辑和组件之间的界限不清。
执行效率低下。这些问题加在一块儿,使得团队很难独立自主地执行任务。
随着Uber从10多个工程师发展到100多个工程师,多个团队拥有技术栈的碎片时,这种单一的架构将团队的命运捆绑在一块儿,使得独立运做变得困难。
所以,咱们采用了微服务架构。最终,咱们的系统变得更加灵活,这使得团队可以更加自主。
系统的可靠性。在微服务架构中,总体系统的可靠性上升。单个服务能够在不影响整个系统的状况下宕机(并被回滚)。
关注点的分离。从架构上来看,微服务架构迫使你去问 "这个服务为何存在?"更加清晰地定义不一样组件的角色。
明确全部权。代码拥有者变得更加清楚。服务一般由我的、团队或组织级别拥有,从而实现更快的增加。
自主执行。独立的部署 更清晰的所属权限,让不一样的产品和平台团队可以自主执行。
开发人员的速度。应用团队能够独立部署他们的代码,这使得他们可以按照本身的项目进度来执行
绝不夸张地说,若是没有微服务架构,Uber不可能达到今天所保持的规模和执行质量。
然而,随着公司规模的进一步扩大,从100多名工程师到1000多名工程师,咱们开始注意到一系列与系统复杂性大大增长的相关问题。在微服务架构下,人们用单一的总体代码库换取了黑盒,黑盒的功能随时可能发生变化,很容易形成意外状况。
例如,工程师们不得不经过12个不一样团队大约50个服务来调查问题的根本缘由。
理解服务之间的依赖关系可能会变得至关困难,由于服务之间的调用可能会深刻许多层。第n个依赖关系的延迟峰值可能会致使上游的一连串问题。若是没有合适的工具,就不可能看到实际发生的状况,这让调试变得困难。
为了构建一个简单的功能,工程师每每须要跨多个服务工做,全部这些服务都由不一样的我的和团队所拥有。这就须要跨部门跨团队的合做,在会议、设计和代码审查上花费时间。因为团队在彼此的服务中构建代码,修改彼此的数据模型,甚至表明服务全部者执行部署,早期对服务全部权的明确界限划分受到了影响。网络化的单体可能会造成,看似独立的服务都必须一块儿部署才能安全地执行任何变动。
这样所带来的结果就是开发进度变慢、服务所属不稳定、迁移更困难等。对于已经采用微服务架构的企业来讲,已经没有回头路了。这就变成了 "有了它们不能活,没有它们不能活"。
面向领域的微服务架构
若是咱们能够将微服务视为I / O绑定的库,而将“微服务架构”视为大型的分布式应用程序,则可使用众所周知的架构来思考如何组织代码。
所以,“面向领域的微服务体系结构”大量借鉴了组织代码的既定方法,例如 域驱动设计 , 清晰架构 , 面向服务的体系架构 以及面向对象和面向接口的设计模式。 咱们认为DOMA仅是创新,由于它是在大型应用的分布式系统中利用既定设计原则的相对新颖的方法。
DOMA相关的核心原理和术语以下:
围绕相关微服务的集合,称为 域
域的集合称之为层。域所属的层肯定了容许该域内的微服务承担什么依赖性,称为 层设计
为域提供接口,这些域被视为集合的单个入口点,称为 网关
肯定每一个域都应该与其余域不可知,一个域不该该具备与其代码库或数据模型内部硬编码的另外一个域相关的逻辑。因为团队常常须要在另外一个团队的域中包含逻辑(例如,自定义验证逻辑或数据模型上的某些元上下文),所以咱们提供了一种 扩展架构 ,以支持该域中定义明确的扩展点。
经过提供系统的体系结构,域网关和预约义的扩展点,DOMA打算将微服务体系结构从复杂的东西转变为可理解的东西:结构化的一组灵活,可重用和分层的组件。
这篇文章的其他部分将深刻研究Uber在DOMA中的实施,咱们已经看到的好处以及为可能但愿采用这种方法的公司提供的实用建议。
Uber的措施
域
Uber域表明一个或多个与逻辑功能分组绑定的微服务的集合。设计域时常见的问题是“域应该有多大?”有些域能够包含数十个服务,有些域只能包含单个服务。重要的任务是仔细考虑每一个集合的 逻辑 角色。例如,咱们的地图搜索服务构成一个域,票价服务是一个域,匹配平台(匹配骑手和驾驶员)是一个域。这些也不老是遵循公司的组织结构。Uber Maps组织自己分为三个域,在三个不一样的网关后面有80个微服务。
层设计
层设计回答了“什么服务能够调用其余什么服务?”的问题。在Uber的微服务架构中,咱们能够将层设计视为“规模化的关注点分离”,或者,咱们能够将层设计视为“规模化的依赖管理”。
层设计描述了一种机制,用于考虑Uber的故障影响范围和跨服务依赖的产品特异性。 当域从底层移到顶层时,它们在中断的状况下会影响较少的服务,并表明更多特定的产品使用案例。 相反,底层的功能具备更多的依存关系,所以趋向于具备更大的影响半径,并表明了更通用的业务功能集。下图说明了此概念。
能够将顶层视为具体的用户体验(例如移动功能),将底层视为通用的业务功能(例如账户管理或市场行程)。层仅取决于其下的层,这为咱们提供了一种有用的启发式方法,能够思考影响范围和区域集成等问题。
值得注意的是,功能常常会从这个图表中 "向下 "移动,从具体到更广泛。能够想象,一个简单的功能,随着需求的变多,最终会变成愈来愈多的平台。事实上,这种向下迁移是意料之中的,Uber的许多核心业务平台一开始都是针对骑手或司机的功能,随着咱们开发出更多的业务线,它们也有了更多的依赖性,就会变得愈来愈通用(好比Uber Eats或Uber Freight)。
在Uber内部,咱们创建了如下五个层次。
基础设施层。 提供任何工程项目均可以使用的功能。这是Uber对诸如存储或网络等重大工程问题的处理。
业务层。 提供应用可使用的Uber功能,但并不是特定于特定产品类别或业务线(LOB)的功能,例如乘车,进餐或货运。
产品层。 提供与特定产品类别或LOB相关但与移动应用程序无关的功能,例如由多个面向应用程序的Rides所利用的“请求乘车”逻辑(Rider,Rider“ Lite”,m.uber.com)等)。
演示层。 提供直接与面向消费者的应用程序(移动/网络)中存在的功能相关的功能。
边缘层。 将Uber服务安全地暴露给外界。该层也支持移动应用程序。
每层表明着愈来愈具体的功能分组,而且影响半径愈来愈小(或者换句话说,更少的组件取决于该层中的功能)。
网关
在微服务架构中相信你们对“API网关”这个术语并不陌生。而在DOMA中咱们的定义的网关其实与你们所熟知的“API网关”的概念相差无几,只是咱们倾向于将网关专门视为 进入 基础服务集合(称为 域) 的 单个入口点****。 网关的成功取决于API设计的成功。
因为上游使用者仅在单一服务上运行,所以网关在迁移,服务发现以及总体系统复杂度方面提供了许多好处,上游服务仅需一个依赖项,而不是对域中可能存在的几个下游服务的依赖。若是咱们从面向对象设计的角度考虑网关,那么它们就是接口定义,它使咱们可以根据底层“实现”(在本例中为底层微服务的集合)作咱们想作的任何事情。
扩展
扩展表示一种 扩展 域的机制。扩展的基本定义是,它提供了一种扩展基础服务功能的机制,而无需更改该服务的实际实现,也不会影响其总体可靠性。在Uber,咱们提供了两种不一样的扩展模型: 逻辑扩展 和 数据扩展 。扩展的概念使咱们可以将架构扩展到可以独立工做的多个团队。
逻辑扩展
逻辑扩展提供了一种扩展服务的底层逻辑机制。对于逻辑扩展,咱们使用 提供程序 或 插件 模式的变体,其接口是以服务为基础定义的。这样一来使得扩展团队能够在不修改底层平台核心代码的状况下,以接口驱动的方式实现扩展逻辑。
例如,一个驱动上线。一般,咱们会进行各类检查以确保容许驱动上线(安全检查,合规性等)。这些都由一个单独的团队拥有。一种实现方法是让每一个团队在同一端点编写逻辑,但这可能会增长复杂性。每次检查都须要自定义且彻底不相关的逻辑。
对于逻辑扩展,“上线”端点将定义一个接口,他们但愿每一个扩展都符合预约义的请求类型和响应。每一个团队都将注册一个负责执行此逻辑的扩展。在这种状况下,他们可能简单地获取一些关于驱动程序的上下文况,而后返回布尔值,来判断驱动程序是否能够上线。“上线”端点将简单地遍历这些响应,并肯定它们其中是否有问题。
这就将核心代码与每一个扩展解耦,并提供了扩展之间的隔离,它不知道其余逻辑在执行什么。围绕这一点,就能很容易创建更多的功能,好比可观察性或者是特征标志等。
数据扩展
数据扩展提供了一种将任意数据附加到接口的机制,来避免核心平台数据模型中的臃肿。对于数据扩展,咱们利用Protobuf的 Any 功能,这样团队能够将任意数据添加到请求中。服务一般会存储这些数据或将其传递给逻辑扩展,这样核心平台就永远不会负责反序列化(从而 "知道")这个任意上下文。Protobuf的任何实现都会有一些基础设施开销,以换取更强的类型化。为了更简单的实现,咱们能够直接使用JSON字符串来表示任意数据。
自定义扩展
在逻辑和数据扩展以外,Uber的不少团队都推出了本身适合本身领域的扩展模式。例如,与咱们的展现架构绑定的不少集成都使用了基于DAG的任务执行逻辑。
效益
Uber几乎每一个主要领域都在必定程度上受到了DOMA的影响。在过去的一年里,咱们主要关注Uber的业务层,它为咱们的各个业务线提供了通用的逻辑。
DOMA在Uber还很年轻,咱们很高兴能在将来分享更多的数据和咱们架构的深刻案例。不过,在简化开发人员体验和下降总体系统复杂度方面,早期的迹象是很是积极的。
产品与平台
DOMA是Uber整个产品和平台团队达成共识的结果。平台支持成本每每降低了一个数量级。产品团队从护栏和加速开发中获益。
例如,咱们扩展架构的一个早期平台消费者经过采用扩展架构,减小了代码审查、规划和消费者学习曲线的时间,可以将一个新功能的优先级和集成时间从三天降低到三个小时。
下降复杂度
之前产品团队要利用一个领域,须要调用许多下游服务,如今只须要调用一个服务。经过减小入驻新功能的接触点数量,平台可以将入驻时间缩短25-50%。此外,咱们可以将2200个微服务划分为70个域。其中大约有50%已经实施,其中大部分有一些将来采用的计划。
将来的迁移
在Uber,咱们计算过微服务的半衰期是1.5年,也就是说每1.5年咱们就有50%的微服务流失。若是没有网关,微服务架构很容易由于这种流失而陷入 "迁移地狱"。不断变化的微服务须要不断进行上游迁移。网关使团队可以避免对底层领域服务的依赖性,这意味着这些服务能够在不强制进行上游迁移的状况下发生变化。
Uber在去年最大的两次平台重写都发生在网关背后。这些平台有数百个依赖于它们的服务,这些服务将不得不迁移现有的平台。在这些状况下,迁移的成本会很是高,使得彻底的平台重变得写不可行。
新的业务和产品线
事实证实,使用DOMA设计的平台可扩展性更强,也更容易维护。Uber的大多数团队之因此采用DOMA,是由于支持新业务线的成本过高。
一些建议
本节为可能想采用DOMA的公司提供一些实用的建议。这里的指导原则是,根据咱们的经验,一个成熟的、通过深思熟虑的微服务架构源于在正确的时间向正确的方向悄悄推敲。现实状况是,对于一我的的整个微服务架构来讲,真正的 "重写 "是永远不可能的。
所以,咱们认为微服务架构的演进更像是 "修剪树篱",使其最终正确成长,而不是自上而下或一次性的架构(或从新架构)工做。这是一个动态和渐进的过程。
创业公司
驱动性的问题应该是 "咱们应该在何时采用微服务架构?"和 "它对咱们的组织有意义吗?" 正如咱们在上面所看到的那样,虽然微服务为拥有大量工程师的组织提供了运营上的好处,但这也换来了复杂性的增长,会使功能的构建更加困难。
在小型公司中,运营效益可能没法抵消架构复杂性的增长。此外,微服务架构一般须要专门的工程资源来支持,这对于早期阶段的公司来讲可能超出了预算,不然从优先级的角度来看是次优的。
考虑到这一点,在一段时间内彻底暂缓采用微服务也不是没有道理的。若是一个组织真的选择采用微服务,就应该考虑 "微服务做为大型分布式应用 "的类比,以及想要构建的微服务之间的关注点分离。另外,要认识到,第一批微服务极可能是最重要的,也是持续时间最长的,由于它们真正描述了业务的核心。
中型
一旦公司的规模达到中等,有了多个团队,不一样的功能和平台之间的关注点明确分离变得朦胧,微服务架构就会变得更加明显有用。
在这个阶段,人们能够开始考虑微服务之间的层次结构。依赖性管理可能会变得更加剧要,由于一些服务开始对业务运营变得更加明显的关键,愈来愈多的团队依赖这些服务。
早期对平台化的投资可能会在将来的道路上获得回报。若是可以建立彻底产品不可知的业务平台,避免核心平台服务中任意的产品逻辑,这里就有可能避免技术债务。此时采用扩展来实现这一目标多是有意义的。
鉴于微服务的数量可能还至关少,将它们集中在一块儿可能没有意义。不过,这里值得注意的是,在Uber的DOMA实现背景下,一个领域能够包含一个服务,因此用 "面向领域 "的方式来思考可能仍是有用的。
大型
规模较大的工程组织可能有数百名工程师和微服务以及多个依赖关系。这时DOMA就达到了它的所有做用。极可能会有明显的微服务集群,这些集群能够很容易地归为域,在它们前面有一个网关。遗留服务每每开始须要重构或重写,而后进行迁移,这意味着若是网关已经到位的话,很快就会开始在迁移的便利性方面提供价值。
明确的层次结构也将变得愈来愈重要,一些服务将做为 "产品 "服务来运行,以实现特定的功能或功能分组,而其余服务将愈来愈多地支持多个产品,并被认为是 "平台"。现阶段关键是要保持任意产品逻辑与平台的脱钩,这样才能避免给平台团队带来沉重的运营负担以及整个系统的不稳定。
最后的感想
随着Uber愈来愈多的团队来采用DOMA,咱们仍在积极地进化DOMA。DOMA的关键洞察力在于,微服务架构实际上只是一个大型的分布式程序,你能够将一样的原则应用于它的演进,就像你应用于任何软件同样。DOMA只是一种在实践中思考这些原则的方法。咱们但愿其余人以为它有用,咱们也期待着反馈。
本文分享自微信公众号 - JAVA高级架构(gaojijiagou)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。