⾯向领域的微服务架构
原⽂地址
摘要
最近有很多关于SOA(⾯向服务架构)的讨论,特别是关于微服务架构的缺点讨论。就在⼏年前,许多⼈还倾向于采⽤微服务架构,由于微服务体架构具备许多优点,⽐如灵活独⽴部署的形式、责任清晰、系统稳定性好和更好的关注点分离。近年来,⼈们开始吐槽微服务的复杂性,有时甚⾄很简单的功都能难以实现。
随着优步发展到⼤约有2200个核⼼微服务时,我们亲⾝体验了这些利弊的权衡。在过去的两年中,优步奋⼒保持微服务架构优势,同时降低微服务的复杂性。通过这篇博客,我们希望介绍关于构建微服务架构的通⽤⽅法,我们将其称为“⾯向领域的微服务架构”(DOMA-Domain-Oriented Microservice Architecture)。
虽然近年来因为微服务的这些缺点,⼤家吐槽微服务架构,但很少有⼈直接主张拒绝微服务架构。因运营效益太重要了,似乎没有或有限的其他选择。关于DOMA的⽬标是为那些希望在保持微服务体架构灵活性的同时降低系统复杂性的组织提供⼀种架构⽅式。
这篇⽂章介绍了DOMA,以及Uber为何采⽤这种架构,它对平台和产品团队的好处,最后是对希望采⽤这种架构的企业⼀些建议。
什么是微服务
微服务是⾯向服务架构(SOA)的扩展。与包含2000个功能的特⼤型“服务”相反,微服务是功能单⼀的服务集合。这些微服务被托管并通过⽹络访问,暴露定义好的接⼝。其他应⽤程序通过RPC/HTTP来调⽤这些接⼝。
微服务架构的核⼼特征是代码管理、服务调⽤和部署⽅式。考虑到⼤型单体应⽤,它们通常被分成具有良好定义的接⼝组件。这些接⼝将在进程内直接调⽤,⽽不是通过⽹络调⽤。通过这种⽅式,我们可以将微服务看作⼀个性能受限(因⽹络I/O和序列化/反序列化)的库,因为需要通过⽹路来访问微服务的功能(函数)。
当我们以这种⽅式考虑微服务时,我们可能会问为什么要采⽤微服务架构。答案通常是能独⽴部署和扩展。对于⼤型单体应⽤,⼀次修改,整个系统被迫重新部署或发布所有代码。系统的每个新版本都可能涉及许多更改。系统部署变得危险、耗时。任何⼈都能搞垮整个系统。
换句话说,团队采⽤微服务是以牺牲性能为代价换取⾼效地部署、扩展等优势。团队还必须承担⽀持微服务所需的基础设施成本。事实证明,在许多情况下,这种利益权衡是很有意义的,但这也是反对过早采⽤微服务体系结构的有⼒论据。
2018年Jaeger的微服务架构
未使⽤DOMA之前,⼀个简单的集成要10个访问⼊⼝
其结果是开发变慢、服务所有者不稳定、迁移痛苦等等。对于已经采⽤微服务架构的组织来说,已经没有回头路了。这就导致⼀种“不能耦合在⼀起,但⼜不能分离“的现象。
⾯向领域的微服务架构
如果我们可以把微服务看作是绑定I/O的库,⽽把“微服务架构”看作是⼀个⼤型的分布式应⽤,那么我们就可以使⽤易于理解的架构来考虑如何组织我们的代码。
因此,“⾯向领域的微服务架构”⼤量借鉴了已有的代码组织⽅式,如领域驱动设计、整洁架构、⾯向服务架构以及⾯向对象和⾯向接⼝设计模式。我们认为DOMA的创新之处在于,它在⼤型组织中开发⼤型分布式系统时使⽤了那些已有的设计原则。
DOMA的核⼼原则和术语如下:
1、我们不是⾯向单个微服务,⽽是⾯向相关微服务的集合。我们称之为领域(domain)。
2、进⼀步创建领域的集合,我们称之为层(layer)。领域所属的层确定了该域中的微服务允许承担的依赖关系。我们称之为分层设计。
3、为这些领域提供整洁的接⼝,作为访问这些领域集合的唯⼀⼊⼝。我们称为⽹关。
4、最后,明确每个领域对其他领域内部是不可知的,也就是说,⼀个领域不应该和另⼀个领域有逻辑关联,或者在其代码库或数据模型中有硬编码。由于团队经常需要在另⼀个团队的领域中包含逻辑(例
如,⾃定义验证逻辑或数据模型上的⼀些上下⽂),我们提供了⼀个扩展架构来⽀持领域内定义良好的可扩展点。
分布式和微服务的关系
换句话说,通过提供⼀个系统架构、领域⽹关和预定义的扩展点,DOMA将微服务架构由复杂转换为可理解的:⼀组灵活、可重⽤和分层的结构化组件。
这篇⽂章的其余部分将深⼊探讨Uber实施DOMA的⽅法,我们已经看到的好处,以及为可能想采⽤这种⽅法的公司提供实⽤建议。
U ber实现
ber实现
D om ains
优步的domains表⽰⼀个或多个功能相关的微服务集合。设计domain时的⼀个常见问题是“domain范围应该有多⼤?”这⾥我们不提供任何指导。有些域可以包含数⼗个服务,有些域只能包含⼀个服务。重要的是仔细考虑每个集合的逻辑⾓⾊。例如,我们的地图搜索服务构成了⼀个domain,票价服务是⼀个domain,匹配平台(匹配乘客和司机)是⼀个domain。这些也并不总是遵循公司的组织结构。优步地图团队本⾝被分成三个domain,在三个不同的⽹关后⾯有80个微服务。
分层设计
分层设计回答了在优步的微服务架构中“什么服务可以调⽤其他服务?”。因此,我们可以将分层设计看作是“规模化的关注点分离”。或者,我们可以把分层设计看作是“规模化的依赖管理”。
分层设计描述了⼀种机制,⽤于考虑Uber服务依赖关系中的故障影响范围和产品专⽤性。随着domain从底层迁移到顶层,它们在停机时影响的服务更少,并代表更具体的产品⽤例。相反,底层的功能具有更多的依赖关系,因此往往具有更⼤的故障影响范围,并代表⼀组更通⽤的业务功能。下图说明了这个概念。
我们可以将顶层看作是特定的⽤户体验(如移动功能),⽽底层则是通⽤的业务功能(如帐户管理或市场旅⾏)。特定层只依赖于它们下⾯的层,这为我们思考故障影响范围和domain集成等问题提供了有益的启发。
值得注意的是,功能通常会从这个图表的特定部分“向下”移动到更⼀般的部分。可以想象,随着需求的发展,⼀个简单的特性最终会越来越像⼀个平台。事实上,这种向下迁移是可预见的,优步的许多核⼼业务平台⼀开始是乘客或司机特定的功能,随着我们发展更多的业务线,它们变得更加⼀般化,并具有更多的依赖性(如Uber送餐或货运)。
在Uber内部,我们建⽴了以下五个层。
1、* 基础设施层。提供任何团队都可以使⽤的功能。这是优步对存储或⽹络等重⼤⼯程问题的答案。
2、* 业务层。提供优步作为⼀个组织可以使⽤的功能,但不只针对特定的产品类别或业务线(LOB),如乘车、餐饮或货运。
3、* 产品层。提供与特定产品类别或业务线相关的功能,但与移动应⽤程序⽆关,如“请求乘车”逻辑。
4、* 表⽰层。提供⾯向消费者的应⽤服务(移动/web)
5、* 边缘层。将优步服务安全地对外开放。这⼀层也是移动应⽤程序感知的。
正如您所看到的,每个后续层都代表了⼀个⽇益特定的功能分组,并且具有越来越⼩的故障影响范围(换句话说,依赖于该层功能的组件越来越少)⽹关
术语“⽹关API”在微服务架构中已经是⼀个⼴泛的概念。我们的定义与已有的定义相差不⼤,只是我们倾向于将⽹关专门视为基础服务集合(我们称之为域)的唯⼀⼊⼝。⽹关的成功依赖于API设计的成功。
上图说明了⽹关功能。它抽象出域的内部细节——多个服务、数据表、ETL管道等。只有接⼝——RPC api、消息事件和
查询公开给其他域
由于上游消费者只在单个服务上操作,上游服务只采⽤单个依赖关系,⽽不依赖某个domain中存在的多个下游服务,因此⽹关在未来迁移、可发现性和整体降低系统复杂性⽅⾯提供了许多好处。如果我们从⾯向对象设计的⾓度来考虑⽹关,那么它们就是接⼝定义,它使我们能够在底层“实现”(在本例中是底层微服务的集合)⽅⾯做我们想做的任何事情。
扩展
扩展:表⽰⼀种扩展领域的机制。扩展的基本定义是,它提供了⼀种机制,⽤于扩展基础服务的功能,⽽不会改变该服务的实际实现,也不会影响其总体可靠性。在Uber,我们提供两种不同的扩展模型:逻辑扩展和数据扩展。扩展的概念允许我们将架构扩展到多个能够相互独⽴⼯作的团队。
逻辑扩展
逻辑扩展为扩展服务的底层逻辑提供了⼀种机制。对于逻辑扩展,我们使⽤提供者或插件模式的变体,并在每个服务的基础上定义接⼝。这使得扩展团队可以以接⼝驱动的⽅式实现扩展逻辑,⽽⽆需修改底层平台的核⼼代码。
例如,⼀个司机上线。通常,我们会进⾏各种检查,以确保司机可以上线(安全检查、合规等)。每⼀个
功能都属于⼀个独⽴的团队。实现这⼀点的⼀种⽅法是让每个团队在相同的⼊⼝编写逻辑,但这可能会引⼊复杂性。每个检查都需要⾃定义、完全不相关的逻辑。
在逻辑扩展的情况下,“上线”⼊⼝将定义成⼀个接⼝,它们希望每个扩展都符合预定义的请求类型和响应。每个团队将注册⼀个负责执⾏此逻辑的扩展。在这种情况下,它们可能只是获取有关司机的⼀些上下⽂,并返回⼀个bool值,表⽰司机是否可以上线。上线服务将简单地遍历这些响应,并确定其中是否有错误。
这将核⼼代码与每个扩展解耦,并在扩展之间提供隔离,⽽不知道正在执⾏的其他逻辑。很容易围绕这⼀点构建更多功能,⽐如可观察性或特性标记。
数据扩展
数据扩展提供了⼀种将任意数据附加到接⼝的机制,避免核⼼平台数据模型的膨胀。对于数据扩展,我们利⽤Protobuf的Any功能,这样团队就可以向请求中添加任意的数据。服务通常会存储这些数据或将其传递给逻辑扩展,这样核⼼平台就不负责反序列化这个任意上下⽂。Protobuf的Any实现带来了⼀些基础设施的开销,为换取更强的类型。对于简单的实现,可以很容易地使⽤JSON字符串来表⽰任意数据。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。