微服务(Microservices)-⽂章翻译
垃圾简书,垃圾CEO,垃圾某豚,从此绝不再⽤。
————————————————————————————————————
微服务架构的定义
“微服务架构”(Microservice Architecture)⼀词在过去的⼏年间涌现出来,作为⼀套可以独⽴部署的服务,⽤来描述⼀种特殊的设计软件应⽤的⽅式。虽然没有这个架构风格没有明确的定义,但围绕业务功能的组织,⾃动部署(automated deployment),端智能(intelligence in the endpoints,)以及对语⾔和数据的分散控制存在某些共同的特征。
“微服务”,⼜⼀个出现在众多软件架构术语中的新词。虽然我们主观倾向于对这样的概念不屑⼀顾, 但这个词描述了⼀种让我们觉得越来越有吸引⼒的软件系统风格。我们已经看到在过去的⼏年间有多个项⽬使⽤这种风格,到⽬前为⽌,结果都是乐观的。以⾄于这已经逐渐成为了我们很多同事在构建企业级应⽤的默认⽅式。然⽽,遗憾的是,并没有太多的信息去概述什么是微服务以及如何去做。
简⽽⾔之,微服务架构风格就是将单⼀应⽤的开发分为多个⼩的服务,每个⼩的服务在⾃⼰的进程中运⾏并使⽤轻量级机制进⾏通信(通常是⼀个HTTP API源),这些服务围绕业务性能进⾏构建,并且通过完
全⾃动化的部署机制独⽴的部署。这些只需要最低限度的集中管理的服务,可以使⽤不同的编程语⾔编写,以及使⽤不同的数据存储技术。
为了阐述微服务架构,有必要让其与Monolithic架构进⾏⽐较:⼀个Monolithic应⽤由⼀个单元构建。企业级应⽤通常由三部分构建:客户端界⾯(包括HTML页⾯和运⾏在客户机器上浏览器⾥的javascript脚本),数据库(许多表构成的、相互关联的关系型数据库管理系统)以及⼀个服务器端应⽤。服务器端应⽤处理HTTP请求,执⾏域逻辑(domian logic),从数据库中查询和更新数据,选择并填充将要发送到浏览器的视图。这个服务器端程度就是⼀个Monolithic - 单⼀的逻辑上可执⾏的应⽤。对这个系统的任何更改都会构建和部署⼀个服务器端应⽤的新版本。
Monolithic架构是构建这样⼀种系统很⾃然的⽅式。处理请求的逻辑都在⼀个单⼀的进程中,允许你使⽤编程语⾔的基本特性将应⽤分割为类,函数以及命名空间。在某些场景中,你可以在开发⼈员的计算机运⾏和测试应⽤,并使⽤部署管道来确保对更改进⾏正确的测试以及部署到⽣产环境中。你也可以横向扩展Monolithic应⽤,通过负载均衡将多个应⽤部署到多个服务器上。
Monolithic架构能够成功 , 但是越来越多的对其感到失望, 特别是越来越多的应⽤被部署到cloud中。变更周期被绑定了,对应⽤⼀个⼩的更改,将会重新构建者部署整个项⽬。随着时间的推移,通常很难保持⼀个良好的模块化结构,这使得更改时只影响应⽤的⼀个⼩模块变得更加困难,扩展时需要扩展整个应⽤,⽽不只是进⾏部分部署。
Monolithic架构的这些缺陷导致了Microservices架构的出现:通过⼀系列的服务去构建应⽤。服务除了可以独⽴部署和可扩展之外,每个服务还有固定的模块边界,甚⾄允许使⽤不同的语⾔去编写不同的服务。它们也能被不同的团队进⾏管理。
我们并不主张说Microservices架构是多么新的东西,它⾄少可以追溯到Unix设计规范之前。但是我们确实认为没有⾜够的⼈去考虑Microservices架构,如果他们使⽤Microservices架构,很多软件系统将会变得更好。
微服务架构的特征
我们不能说微服务架构有⼀个正式的定义,但我们可以去尝试描述与“微服务”这个标签相匹配的体系架构的共同特征。正如⽤⼀个定义概括任何其他具有共同特征的事物⼀样,并⾮所有的微服务架构具有这些所有的特征,但我们确实希望所有的微服务架构具有⼤多数的这些特征。
尽管笔者们已经是这个相当松散的社区的积极成员,我们的意图是尝试去描述在我们⾃⼰的⼯作中所发现的,以及我们所知道的其他团队类似的努⼒。特别是我们没有下明确的定义遵守这个架构的时候。
——————————————————————————————————————
组件化(Componentization) 与服务 (Service)
⾃从我们开创软件⾏业以来,就会⼀直希望将组件连接在⼀起的⽅式去构建系统,就像我们在物理世界中所看见的⼀样。在过去的⼏⼗年中,我们看见了⼤量简编的公共库取得的巨⼤进步,它们是⼤多数语⾔平台的⼀部分。
在谈论组件时,我们遇到了定义上的难题即什么构成了组件。我们的定义是,组件是⼀个独⽴的,可替换的和可升级的软件单元 。
微服务架构会使⽤库,但是其组件化⾃⾝软件的主要⽅式是将其拆分为服务。我们将库定义为连接到程序中的组件,并使⽤内存中的函数进⾏调⽤,⽽服务则是进程外的组件,这些组件通过webservice请求或者远程过程调⽤(RPC)等机制进⾏通信。(组件和服务在很多⾯向对象编程中是不同的概念)
使⽤服务作为组件(⽽不是库)的主要原因是服务是可以独⽴部署的。如果你的应⽤由单个进程中的多个库组成,则对单个组件的任何更改都将导致不得不重新部署整个应⽤。但是,如果将应⽤分解为多个服务,你可以期望单个服务的更改只需要重新部署该单个服务即可。当然,这也不是绝对的,⼀些更改将改变服务接⼝,从⽽会产⽣⼀些协调。但是优秀的微服务架构的⽬的是通过服务契约中的解耦服务边界和进化机制将这些更改最⼩化 。
使⽤服务作为组件的另⼀个结果是更显式的组件接⼝。 ⼤多数语⾔都没有良好的机制来定义显式的发布接⼝。通常只有⽂档化和规则才能防⽌⽤户破坏组件的封装,从⽽避免组件之间的紧密耦合。通过使⽤显式的远程调⽤机制,服务可以更容易的避免这种这种情况。
使⽤这样的服务确实有缺陷。远程调⽤⽐在进程内调⽤更消耗资源,由于远程API需要粗粒度的,这通常更难于使⽤。如果你需要更改服务之间的职责分配,那么当你跨越进程边界时,这种⾏为⽅式将会变
得更加困难。
在第⼀种类似情况中,我们注意到服务映射到运⾏时进程,但这只是第⼀种类似情况。服务可以由多个进程组成,这些进程将始终被开发和部署在⼀起,例如应⽤程序进程和仅由该服务使⽤的数据库 。
——————————————————————————————————————
围绕业务功能组织
当将⼀个⼤型应⽤分为多个部分时,通常管理会集中在技术层⾯,UI团队,服务端团队和数据库团队。当团队沿着这条线分开时,即使是简单的变更也会导致跨团队间的项⽬时间和预算审批的花费。⼀个聪明的团队会优化这个问题-两害相权取其轻-只需将逻辑置于它们想要访问的应⽤中。换句话说,逻辑⽆处不在。这是Conway定律得⼀个例⼦。
微服务的划分⽅法是不同的,它倾向于围绕业务功能的组织来分割服务。此类服务对该业务领域进⾏了⼴泛的软件实现,包括⽤户界⾯、持久化存储和任何的外部协作。因此,团队是跨职能的,包括开发过程所需的全部技能:⽤户体验、数据库和项⽬管理。
有⼀个以这样的⽅式进⾏组织的公司 :wwwparethemarket 。跨功能团队负责构建和执⾏每个产品,并将每个产品分割成多个单独的服务,通过消息总线进⾏通信。
⼤型的Monolithic应⽤也可以围绕业务功能进⾏模块化,尽管这不是常见的情况。 当然,我们会敦促⼀
个构建Monolithi应⽤的⼤型团队,以便将其沿着业务线进⾏划分。
我们在这⾥看到的主要问题是,这种组件形式会导致太多的依赖。如果Monolithic跨越了许多模块化的边界,那么团体的个体成员很难将其融⼊到他们的短期记忆中。此外,我们还发现,模块化需要⼤量的规则来执⾏。服务组件要求的分割越明确,使得保持团队之间的界限变得越容易。
——————————————————————————————————————
微服务有多⼤?
尽管“微服务”已经成为了这种架构风格的流⾏名称,但是它的名字确实导致了对服务规模的不幸关注,以及关于什么构成“微”的争论。在我们与微服务实践者的交流中,我们看到了⼀系列服务的⼤⼩。 据报道,
最⼤的规模遵循了亚马逊“Two Pizza Team”的概念(即整个团队两个⽐萨就够了),也就是不超过12个⼈。在规模较⼩的团队⾥,我们看到的是⼩规模的团队提供⼩规模的服务这种配置。
这样就引出了⼀个问题:在这个范围内是否存在⾜够⼤的差异,即每个成员的服务和每个团队的服务的⼤⼩不应该被归⼊⼀个微服务标签。⽬前,我们认为最好将它们组合在⼀起,但当我们进⼀步探索这种架构风格时 ,我们当然有可能改变我们的想法。
——————————————————————————————————————
产品不是项⽬
我们看到的⼤多数应⽤开发⼯作都使⽤了⼀个项⽬模型:⽬的是为了交付⼀些软件,然后就被认为是完成了。软件完成后被移交给维护部门,构建软件的项⽬组解散。
微服务的⽀持者们倾向于避免这种模式,他们更倾向于⼀个团队应该负责产品的整个⽣命周期。这是⼀个共同的灵感,是亚马逊“你构建,你运维”的概念,⼀个开发团队对软件的开发承担全部的责任。
这使得开发⼈员每天接触到他们的软件在开发过程中的⾏为,并增加与⽤户的联系,因为他们⾄少要承担⼀些售后⽀持负担。
产品的理念,与业务功能联系在⼀起。并不只是将软件看成⼀组要完成的功能,还有⼀个持续性的问题,即软件如何帮助⽤户提⾼其业务能⼒。
我们并没有理由不能使⽤Monolithic应⽤,但是服务的粒度越⼩,就越容易在服务提供者和⽤户之间建⽴紧密关系。
——————————————————————————————————————
强化终端及弱化通道(Smart endPoints and dumb pipes)
在构建不同进程间的通信结构时,我们发现很多产品和⽅法能够更加有效的将其⾃⾝加⼊到通信机制中。其中⼀个很好的例⼦是企业服务总线(ESB),ESB产品通常包括⽤于消息路由,编排,转换和应⽤业务规则。
微服务社区⽀持的另⼀种⽅法:强化终端及弱化通道 。从微服务中构建的应⽤的⽬标是尽可能的解耦和尽可能的内聚-它们拥有⾃⼰的域逻辑,就像在经典的Unix意义上的过滤器-接收请求,适当的应⽤逻辑并⽣成响应。这些都是使⽤简单的RESTish协议编排的,⽽不是像WS-Choreorgaphy或BPE或者由集中式框架编制的复杂协议,最常见的两种协议是携带资源的HTTP请求-响应以及轻量级的消息传递。
——————————————————————————————————————
微服务和SOA
当我们讨论微服务时,⼀个常见的问题是:这是否仅仅就是我们10年前看到的⾯向服务的架构(SOA)。这⼀点是有价值的,因为微服务的风格⾮常类似于SOA的⼀些⽀持者所倡导的。然⽽,问题是SOA意味着太多不同的东西,⽽且⼤多数时候我们遇到的东西被成为“SOA”,它与我们在这⾥描述的风格有很⼤的不同,通常是因为ESB⽤于集成Monolithic应⽤。
特别是我们已经看到了许多⾯向服务的拙劣实现-从ESB中隐藏复杂性的倾向-到那些花费数百万美元但并⽆交付价值,到积极抑制改变的集中管理模式,有时很难看到过去的这些问题。
当然,在微服务中使⽤的许多技术都是从在⼤型组织中集成服务的开发⼈员的经验中成长起来的。Tolerant Reader模式就是⼀个例⼦。使⽤web 的努⼒已经做出了贡献,使⽤简单的协议是从这些经验中获得的另⼀种⽅法 - 从那些已经达到复杂性的中⼼标准,坦率的说,是惊⼈的。(任何时候,你需要⼀个本体来管理你的本体,你知道你⾝陷困境)。
SOA的这⼀常见表现致使⼀些微服务的拥护者完全拒绝SOA,尽管其他⼈认为微服务是SOA的⼀种形式,或许⾯向服务是正确的。⽆论怎
样,SOA都意味着不同的东西,这意味着⼜⼀个更清晰的定义这个架构风格的术语是很有价值的。
微服务团队使⽤万维⽹(很⼤程度上是Unix)构建的原则和协议。经常使⽤的资源可以通过开发⼈员或者操作⼈员很少的努⼒来缓存。
第⼆种⽅式是在轻量级消息总线上进⾏消息传递。这种通信协议⾮常单⼀(单⼀到只负责消息路由)- 简单的实现⽐如 RabitMQ 和ZeroMQ甚⾄没有提供可靠的异步机制,以⾄于需要依赖产⽣或者消费信息的终端或者服务来处理这类问题。
进程通信方式
在Monolith架构中,组件在进程中执⾏,它们之间的通信通过⽅法调⽤或者函数调⽤来进⾏。将Monolith架构转换为微服务架构的最⼤问题在于改变通信模式。从内存调⽤到RPC的转换会导致不好的信息通信。相反,你需要使⽤粗粒度的通信⽅式来代替细粒度的通信。
——————————————————————————————————————
去中⼼化管理
去中⼼化管理的结果之⼀是在单⼀的技术平台上进⾏标准化的趋势。经验表明,这种⽅法是相对的-并不是所有的问题都是钉⼦,并不是每个解决⽅案都是锤⼦。我们更喜欢使⽤合适的⼯具来来完成⼯作,⽽Monolithic应⽤可以在⼀定程度上利⽤不同的语⾔,这并不常见。
将Monolithic的组件拆分为服务,我们可以在构建它们的时候有⼀个选择。你想要使⽤Node.js来建⽴⼀个报表页⾯?去吧!对于⼀个⼏乎接近实时的组件使⽤C++ ? 很棒。你想要换⼀种不同的数据库,以便更好的适应⼀个组件的读取⾏为吗?我们有重新构建它的技术。
当然,仅仅是因为你可以这样做,并不意味着你应该这样做-但是以这种⽅式划分的系统意味着你有选择。
构建微服务的团队也喜欢⽤不同的⽅法来达到标准。与其将⼀组定义的标准写在纸上,它们更倾向于使
⽤其他开发⼈员可以使⽤的有⽤⼯具来解决他们⾯对的类似问题。这些⼯具通常从实现中获取,并与更⼴泛的团队共享,但不完全使⽤内部的开源模型。现在,git和github已经成为了事实上的版本控制系统,开源实践正变得越来越普遍。
Netflix是遵循这种理念的⼀个很好的例⼦。共享有⽤的,最重要的是,当代码库⿎励其他开发⼈员以类似的⽅式解决类似的问题时,如果需要的话,还可以选择不同的⽅法。共享库往往集中在数据存储、进程间通信等常见问题,以及我们在下⾯讨论的基础设施⾃动化。
对于微服务社区来说, 开销问题是特别引⼈注意的 。 这并不意味着社区不重视服务 交互的价值。 恰恰相反,因为发现它们的价值。这使得它们在寻各种⽅法解决它们。像 Tolerant Reader Pattern和Consumer_Driven Contracts Pattern 这样的模式经常被应⽤到微服务中。这些模式解决了独⽴服务在交互过程中的交互问题。作为构建的⼀部分,使⽤Consumer_Driven Contracts 增强了信⼼,并提供了关于服务是否运⾏的快速反馈。事实上,我们知道澳⼤利亚⼀个团队,他们使⽤Consumer_Driven Contracts 来推动新服务的建⽴。它们使⽤简单的⼯具来定义服务的接⼝。 在新服务的代码编写之前,就已经成为了⾃动化构建的⼀部分。然后,只需要指出这些接⼝的适⽤范围,这是⼀种优雅的⽅法,可以避免在构建新软件时出现"YAGNI"困境。这些技术和⼯具在使⽤过程中完善,通过减少服务之间的耦合来限制集中式 管理的需求。
也许去中⼼化最⾼点是亚马逊推⼴的 build it/ run it理念。团队负责他们构建的软件的所有⽅⾯,包括全天候的运营软件。移交这⼀级别的责任肯定不是常态,但是我们确实看见越来越多的公司将责任推给开发团队。Netflix是另⼀个采⽤这种理念的组织。每天晚上3点被你的寻呼机吵醒,毫⽆疑问是你在编写代码时专注质量的强⼤动⼒。这些想法与传统的中⼼化管理模式相去甚远,然⽽它是可能的。
——————————————————————————————————————
去中⼼化的数据管理
数据管理的去中⼼化有许多不同的⽅式。在最抽象的层次上,这意味着通⽤概念模型将在系统之间有所不同。在跨⼤型企业集成时,这是⼀个常见的问题,客户的销售视图与⽀持视图不同。在销售视图中,⼀些被称为客户的东西可能就根本没有出现在⽀持视图中。那些可能有不同的属性和(更糟糕的)公共属性,具有微妙的不同语义。
这个问题在应⽤之间很常见,但是也会发⽣在应⽤内,特别是应⽤被划分为独⽴的组件时。⼀种有⽤的思考⽅式是有界上下⽂(Bounded Context)的领域驱动设计概念。领域驱动设计将⼀个复杂的域分割成多个有界的上下⽂,并映射出它们之间的关系。这个过程对Monolithic架构和微服务架构都很有⽤,但是在服务和上下⽂边界之间存在⾃然的相关性,正如我们在业务功能所描述的那样,强⾏进程拆分。
除了决定将概念模型去中⼼化,微服务还决定将数据存储的去中⼼化。虽然Monolithic应⽤更喜欢单⼀逻辑的数据库来持久化数据。但企业更喜欢跨多个应⽤的单⼀数据库。许多决策也通过供应商的商业模式授权来驱动。微服务更喜欢让每个服务管理⾃⼰的数据库,或者同⼀数据库技术的不同实例,⼜或者完全不同的数据库系统 - ⼀种被称为多持久化(Polyglot Persistence)的⽅法。你可以在Monolithic架构中使⽤多持久化的⽅法,但是它更频繁的出现在微服务中。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论