Martin Fowler: 微服务

这种新架构术语的定义

在过去几年中,“微服务架构” 这个术语雨后春笋般地涌出来,它描述了一种将软件应用程序设计成一组独立部署的服务的特定方式。虽然当前还没有对这种架构的精确定义,但它们还是有些公共的特征:围绕组织架构和业务能力、自动部署、端点智能化以及语言和数据去中心化控制。


出自微服务部落:https://blog.idevfun.io/martin-fowler-microservices/
本文译自:https://martinfowler.com/articles/microservices.html
原文作者:James Lewis 和 Martin Fowler
翻译:董干


“微服务” —— 软件架构拥挤街道上的另一个新术语。尽管通常我们对这种新事物的自然倾向是轻蔑一瞥然后将它一带而过,然而我们发现这个术语描述了一种越来越吸引人的软件系统风格。我们看到使用这种风格取得了正面效果的项目有很多,多到对于我们很多同事来说已经成了构建企业级应用的默认风格。然而,可惜的是关于指出这种风格是什么以及如何使用这种风格的信息还不是很多。

简短地说,微服务架构风格[1]是一种将应用程序以一种成套的微小服务进行开发的方法,这些微小的服务每个都在自己的进程中运行,并且互相之间通过一些轻量的机制进行通信,通常是 HTTP 资源 API。这些服务围绕业务功能进行组织,并且可以通过完全自动化的设施独立部署。这些服务存在一个可能由不同语言并使用不同的存储技术编写的最小化、中心化的管理设施。

microservices-sq
我写了微服务手册这篇文章,聚合了一些关于微服务的最好的文章、视频、书籍、播客等。

对照着单体风格能更好地解释微服务风格:单体应用是单个单元构成的应用。企业级应用通常由三个主要部分组成:客户端用户交互界面(由 HTML 页面和运行在用户机器上浏览器中的 javascript 组成)、数据库(由很多张表组成的、通常是关系型的数据库管理系统)以及服务端应用。服务端应用处理 HTTP 请求,执行业务逻辑,获取并更新数据库中的数据,并选择、渲染 HTML 视图发送给浏览器。这种服务端应用是单体应用 —— 单一的逻辑可执行应用程序[2]。对系统做的任何改动都涉及到重新构建并部署一个服务端新版本。

这种单体服务是种构建这种系统的很自然的方式。所有处理请求的逻辑运行在单进程中,可以使用你所用语言的基本特性将应用程序划分为类、函数和命名空间。稍加小心你就可以在开发者的笔记本电脑上运行和测试这个应用,并使用部署的 pipeline 确保改动经过充分地测试并且部署到生产环境。你可以启动该单体应用的多个实例,挂在负载均衡后面,实现对单体应用的水平扩展。

单体应用可以非常成功,但越来越多的人对它感到失望 —— 尤其当越来越多的应用开始往云上部署。改动周期全部绑在一起 —— 应用程序一小部分的一点小改动,也需要重新构建和部署整个应用。久而久之,维护一个好的模块化的机构通常变得很难,使得很难将改动的影响范围控制在本应属于的模块。应用扩容需要将整个应用程序一起扩容,而不是仅扩容需要占用很多资源的部分。

sketch-2-cn

图1: 单体应用和微服务

这些沮丧引领人们转向了微服务架构风格:将应用构造成一组服务。这些服务不仅独立部署和扩展,而且每个服务还提供了牢固的模块边界,甚至还可以使用不同的语言来编写不同的服务。这些服务也可以被不同的团队管理。

我们并不认为微服务风格是新事物或是一个创新,它的根源至少可以追溯至 Unix 的设计理念。但是我们确实认为并没有足够多的人考虑使用微服务架构,以及很多软件开发如果使用了这种风格将会更美好。

微服务架构的特征

虽说不算微服务架构风格的正式定义,我们尝试给出我们眼中认为符合这个标签架构的一些公共特征。同其他总结公共特征的定义一样,并非所有微服务架构都同时具有所有的特征,但我们希望大部分微服务架构都能展示出绝大部分特征。尽管作为作者我们是这个相对松散社区的活跃成员,我们的目的是尝试描述在我们自己和所知其他团队相似工作的所见。特别值得说明的是,我们并不是想给出一些需要一定遵守的定义。

通过服务实现组件化

自涉足软件行业以来,我们就希望软件像真实物理世界一样能够通过组合插件的形式构造系统。最近几十年,我们已经见证了公共组件库成为很多语言的组成部分。

尽管困难,但当讨论组件时,我们不得不对组件做一个定义。我们的定义是,组件是能够独立替换和升级的软件单元。

微服务架构也使用库,但是它们进行软件组件化的主要方式是将系统拆散成服务。我们将库定义为链接到程序并且通过进程内函数的方式调用;服务则是通过 web service 或是远程过程调用请求和程序进行通信的进程外组件。(这同很多面向对象语言里的服务对象[译者注:service object]是不同的概念[3]。)

使用服务(而非库)作为组件的一个主要原因是服务能够独立部署。而包含很多库的单进程应用[4]中的任何一个组件的改动都会导致整个应用的重新部署。如果这个应用分解为多个服务,可以期待很多改动只需要重新部署改动到的单个服务。不过这并非绝对,一些改动将会改变服务接口,从而会连带影响一些服务,但是一个好的微服务架构的目的就是要通过合理制定服务边界和服务契约的升级机制来最小化这种影响。

使用服务作为组件的另一个后果是更清晰的组件接口。许多语言没有一个定义清晰明确公开接口的机制。通常仅仅通过文档和纪律约束阻止使用者打破组件的内聚性,导致组件之间过紧的耦合。服务化通过使用明确的远程调用机制使得这种情况难于发生。

这种服务化的方式也有缺点。远程调用远比进程内调用代价高,因此远程 API 通常设计得粒度更粗,通常也就更难使用。对于跨进程边界来说,移动调整组件之间的功能划分也更难于实施一些。

一开始我们可能会观察得出服务和运行时的进程一一对应,但这其实仅仅是个整体来说的近似情况。一个服务也可能由多个同时开发和部署的进程组成,例如一个应用程序进程和仅为该服务提供调用的数据库。

围绕业务功能进行组织

当把一个大的应用程序分成很多部分时,通常管理会按照技术分层,导致分为 UI 团队,服务端业务研发团队,以及数据库团队。当按这种方式组织团队时,最简单的改动也可能引发跨团队的协作,通常来说这种团队间的协作不仅耗时,而且可能会涉及投入资源的预算审批等。聪明的团队通常会在这件事上做些优化 —— 把逻辑强制放在自己可以控制的一侧。这将导致到处都有此业务逻辑。这是康威定律[5]在实践中的体现。

任何组织对(广泛定义的)系统的设计都会最终产出一个和它组织沟通架构相对应的结构。

-- Melvyn Conway, 1967

conways-law-cn

图2: 康威定律实例

微服务方式有着不同的划分方式,它将各服务围绕业务功能进行组织和划分。这些服务使用不同的语言和框架实现这一块业务逻辑,包括用户界面,持久化存储,以及任何外部协同部件。因此团队的组织架构围绕功能而进行划分,每个团队都有完整技能栈的组成者:用户体验,数据库,以及项目管理。

PreferFunctionalStaffOrganization-cn

图3: 团队组织架构边界加强服务边界

一个使用这种组织架构的公司的例子是 www.comparethemarket.com。跨职能团队负责构建和运维每个产品,每个产品分为一些通过消息总线通信的独立的服务。

大型单体应用也可以围绕业务功能进行模块化划分,但实际情况往往不是这样。我们当然希望大型团队按照业务线划分构建单体应用,但我们在这里看到的主要问题是,它们通常会围绕太多上下文进行组织。如果一个单体应用横跨许多这种模块边界,那么团队个体将很难凭借自己的所知把模块放入合适的位置。除此之外,我们发现维护模块边界需要很多纪律上的强制约束。而服务化组件必要的显式分隔使得维持团队边界更为容易。

产品而非项目

我们看到许多程序开发实践使用项目制:目标就是交付软件的某些功能,一旦完成后就认为目标达成。当完成后,软件递交给一个维护团队进行后续维护,原始的项目开发团队被抛弃(译者注:分配至其他资源需求方)。

微服务的倡导者倾向于避免这种开发模式,他们更倾向于团队应该负责产品的整个生命周期。一个常见的启发是 Amazon 的名言 “谁造的,谁来跑”,说的是软件开发团队负责产品在生产环境的所有事情。这也会使开发者每天都会面对一些产品在生产环境表现如何,以及同用户更多的接触,因为他们也将承载一部分用户支持的压力。

产品心态同业务功能紧密相连。软件并不只是一堆功能的集合,它同开发者的关系还将持续关联,体现在软件如何帮助它的用户提升业务能力。

为什么同样的方法不能用在单体应用上,而更细粒度的服务化可以使服务开发者和这些服务的使用者建立联系,这其中的原因并没什么特别的。

端点智能化和管道简单化

当构建进程之间的通信结构时,我们看到很多产品和方法将相当复杂和智能的逻辑放入通信机制本身。一个很好的例子是企业服务总线(ESB),ESB 产品通常包含用于消息路由、编排、转换以及应用业务逻辑规则等复杂的设施。

微服务和 SOA

当我们谈论微服务时,一个常见的问题是,微服务是否就是十年前我们看到的面向服务架构(SOA)。这个观点并不奇怪,因为微服务风格很像某些倡导者支持的 SOA。然而,问题在于 SOA 包含的非常广泛,而且大多数时候我们遇到的所谓 “SOA” 的叫法实际上与我们这里描述的微服务风格有很大的不同,通常在于前者更倾向于关注通过企业服务总线的使用来整合多个单体应用。

特别地,我们看到很多面向服务的拙劣实现——从隐藏 ESB 复杂性的倾向[6],到白白花费数百万却没有价值的失败尝试,到不断阻碍变化的中心化治理模型,以致很多时候我们很难忽视这些问题。

当然,微服务社区中使用的很多技巧来自于开发者在大企业中服务集成积累的经验。容错读取(Tolerant Reader)模式就是其中的一个例子。人们在 Web 使用上的努力做出了贡献,使用简单协议是从这些经验中得出的另一种方法 —— 逃离繁复的集中式标准后的最大感受是神清气爽。(当你需要一个概念来管理另一堆概念时,你懂得有多棘手。)

SOA 的这种公共的表现形式使得一些微服务的倡导者拒绝使用 SOA 的任何标签,尽管有些人觉得微服务就是某种形式的 SOA [7] —— 也许是正确实现了面向服务的 SOA。不论如何,SOA 指代广泛这一事实意味着使用一个术语能更简明扼要地定义这种架构风格还是很有价值的。

微服务社区更喜欢另一种方式:端点智能化,管道简单化。高内聚低耦合是通过微服务构建的应用的一个目标 —— 服务拥有自己的业务逻辑,表现得像是经典 Unix 中的 filter:接收请求,处理合适的业务逻辑,返回响应。它们之间使用简单的类 REST 协议编排,而非复杂的 WS-CDL 或 BPEL 或其他的中心化协调工具。

微服务使用最多的两种协议是基于 HTTP 请求/响应的资源 API,以及轻量的消息队列[8]。对于第一种方式最好的表述是

成为 web 的一部分,而不是 web 的后端。

-- Ian Robinson

微服务团队使用 web (广泛地说,Unix)所基于的原则和协议。例如开发人员和运维人员投入很少的工作量就可以将经常使用的资源做缓存。

另一种常用的方式是基于轻量级消息总线的消息机制。通常选择的基础设施都较为简单(简单到只用做消息路由使用)—— 像 RabbitMQ 或 ZeroMQ 这样的简单实现仅仅是提供了一个可靠的异步传输通道 —— 智能化的部分仍旧在生产和消费这些消息的端,即服务中。

在单体应用中,组件运行在进程内,组件间的通信通过方法调用或函数调用完成。将单体应用转化为微服务的最大问题就是更改通信模式。从内存内的方法调用到 RPC 的一个幼稚的转化导致通信像细碎的闲聊一样,并不能很好适用。因此,你需要将这种频繁细粒度的方法调用转化为粗粒度的方式。

去中心化治理

集中式治理的一个后果是对单一技术平台的逐渐倾向。经验表明这种方式碍手碍脚 —— 并不是所有问题都是一个钉子,而所有问题都是一个锤子。我们更喜欢使用正确的工具去解决问题,虽然单体应用在某种程度上可以使用多语言,但并不常见。

当我们构建将单体应用的组件切分出来形成服务时,我们可以选择。你想用 Node.js 架设一个简单的报表页面?没问题。想使用 C++ 来做一个有挑战的高实时性的组件?没问题。想把一个组件的存储换成其他的数据库来更好地符合其读取特点?我们也有重建它的技术。

当然,仅仅是你可以做这些事,并不意味着你应该这么做 —— 但是将系统按这种方式划分,你至少有选择可以这么做。

构建微服务的团队也更喜欢不同于标准化一切的方式。他们更喜欢制造一些能帮助其他人解决类似于他们面对问题的有用工具,而不是使用一些在论文中规定的标准。这些工具通常来自于实现并和更广泛的团队共享,通常是但不限于使用内部开源的模式。在 git 和 github 已经成为事实上的版本控制选择,开源实践在企业内也越来越常见。

Netflix 就是一个遵守这种哲学企业的例子。将有用的,更重要的是经过大规模生产验证的代码共享为开源库鼓励其他开发者在解决类似问题时使用类似方式,但仍然留出需要时替换为其他方法的可能性。共享库倾向关注数据存储,进程间通信以及下面我们将要深入谈论到的基础设施自动化等公共的问题。

对于微服务社区来说,无关消耗很不受欢迎。这并不代表社区不看重服务间契约。相反地,由于服务间交互会更多,服务间契约更被看重了。只是他们寻求管理这些契约的不同方式。像容错读取消费驱动契约这些模式通常会应用在微服务中。这些模式使得服务契约得以独立地进化。将执行消费驱动契约作为构建过程的一部分增加服务的信心,并提供了服务是否正常工作的快速反馈。我们实际上了解到一个澳大利亚的团队在构建新服务时使用了消费驱动契约。他们使用简单的工具用来定义服务之间的契约。这甚至成为新服务在开始编写前自动化构建的一部分。在这之后服务只会按照契约约定的方向逐渐开发成型 —— 一种避免构造新软件时发生 “YAGNI”[9] 窘境的优雅方式。这些技巧以及围绕他们的工具链支持通过降低服务间的耦合度使得中央契约管理的需求降低。

多语言提供多种选择
JVM 平台的发展是在一个通用平台上混合多种语言的最新的例子。利用高级抽象特性就使用高级语言,而编写性能攸关代码就使用底层语言,这种通行实践已经持续了数十年。然而许多单体应用并不需要这一级别的性能优化,DSL 和高级语言抽象也(令我们震惊般地)并非那么普及。单体应用因此使用单一的语言,并倾向于限制其中使用的技术种类[10]

也许高度去中心化治理的一个提现是 Amazon 推动流行的“谁造的,谁来跑”风潮。开发软件的团队同时负责包括 7x24 运维软件等的各种事项。这种权责下放显然并非普遍做法,但是我们确实看到越来越多的公司将责任移到开发团队上。采用了这种理念的组织还有 Netflix[11]。每天凌晨3点都被报警叫醒显然会强烈促使你在写代码时更关注代码质量。这些理念与传统的中心化治理模型有很大的差异。

去中心化数据管理

去中心化的数据管理表现在一些不同的方面。在最抽象的层面,它意味着不同系统对世界的概念模型都是不同的。这是跨大型企业集成时一个普遍的问题,正如销售对待顾客的视角和客户支持对待顾客的视角不同一样。销售视角中一些被称为客户的东西可能在客户支持视角中根本不会出现。它们可能有各自不同的属性,以及(更糟地)有语义上有细微差异的共同的属性。

实践验证的标准和强制实施的标准
微服务团队倾向于避免使用企业架构师们强制推行的严格标准,但是却很喜欢使用甚至传播像 HTTP,ATOM 和其他微型格式这样的开放标准。这种态度似乎对比鲜明。

这里主要的区别是这些标准是如何开发以及如何实施的。IETF 这类组织管理的标准只有当多个实现已经存在于现实世界时才成为标准,且通常自开源项目成长而来。

这些标准的世界同企业世界的标准不同,后者通常由近期已经不在一线编程的一组人提出,或者过于受到某一实现厂商的影响。

这一问题在不同应用程序之间很普遍,但也可能发生在应用程序内,尤其是当应用程序划分为不同的组件时。一种考虑这种问题的方式是使用领域驱动设计(DDD)的有界上下文概念。DDD 将一个复杂的域划分为多个有界上下文,并理出它们之间的关系。这一过程对于单体应用和微服务架构都适用,但是正如我们即将在下一节业务能力中描述的那样,服务同上下文边界之间有种天然的关联,这种关联使得这种划分变得更清晰,并增强了这种划分。

微服务去中心化决策的不仅是概念模型,还包括对数据存储决策的去中心化。单体应用通常倾向于使用单个数据库存储持久化的数据,而企业通常倾向于对多个应用程序使用同一个数据库 —— 很多这种决策源自于数据库厂商的商业模型许可。微服务倾向于每个服务管理自己的数据库,可以是同一数据库技术的不同实例,也可以是完全不同的数据库系统 —— 一种称为多语言持久化的方式。你也可以在单体应用中使用多语言持久化,但是这种方式在微服务中更为常见。

decentralised-data

图4: 单体应用和微服务的数据库

将跨服务的数据去中心化职责分离对数据更新会产生影响。一直以来更新多个资源时保证一致性的通常方式是使用事务。这也是单体应用通常使用的方式。

使用如上所述的事务有助于数据一致性,但是暴露出重大的时间耦合性,在跨服务时会出现问题。分布式事务臭名昭著地难于实现,因此微服务架构强调服务间的无事务同步,并明确一致性可能只能做到最终一致性,通过补偿操作处理一致性问题的认知。

选择这种管理非一致性的方式对于很多开发团队来说是个新的挑战,但这通常是符合业务实践的一种方式。通常地,为了实现快速响应,业务操作可以容忍一定程度的非一致性,当出现错误时采取某种回退措施来处理。只要修复错误的花费比由一致性所造成的业务损失小,这种折中的权衡就是值得的。

基础设施自动化

基础设施自动化技术在近些年有了极大的发展 —— 云计算尤其是 AWS 的发展减少了构建,部署和运维微服务的运维复杂度。

使用微服务的很多产品和系统都是由有着丰富的持续交付,以及它的前驱,持续集成经验的团队构建。用这种方式构建软件的团队使用了大量的基础设施自动化技术。下面的构建 pipeline 描述了这种情况。

basic-pipeline

图5: 基本的构建 pipeline

因为这不是一篇讲持续交付的文章,我们只在这里提几个核心的特性。我们想对于软件能正常工作尽可能地有信心,因此我们运行很多自动化测试。将软件沿着 pipeline 向上推举意味着我们能够自动地部署到每一个新的环境中。

使正确的事更容易做

我们发现通过持续交付和部署来增强自动化带来的一个副作用是制造帮助开发者和运维的有用工具。构建产出物,管理代码仓库,启动简单的服务以及添加标准的监控和日志的工具现如今已经非常普遍。网上的最好的例子莫过于 Netflix 的开源工具集,但是除此之外还有更多,包括我们广泛使用的 Dropwizard

单体应用的构建、测试以及向这些环境的推送将会非常顺利。事实上当你在对单体应用的自动化之路投资后,部署更多应用似乎并不那么可怕了。记住,CD(持续交付)的目标之一是使部署变得无趣,因此只要仍然无趣,那么不论是部署一个还是三个应用都没有关系[12]

我们看到团队广泛使用自动化基础设施的另一个领域是在生产环境中管理微服务。只要部署仍然无趣单体和微服务就没有那么大的差别,同上面得出的此结论截然不同的是,在运维的领地则可能会大相径庭。

micro-deployment

图6: 模块的部署通常不同

容错设计

使用微服务作为组件的一个后果是应用需要采用可容忍服务失败的设计。任何服务调用都有可能因为服务提供者不可用而失败,客户端需要尽可能优雅地应对这种情况。由于处理这种情况引入了额外的复杂度,因此,相比单体应用来说这是个缺点。后果是微服务团队不停地应对因为服务失败造成的用户体验影响。Netflix 的 Simian Army 通过在工作日引入服务不可用甚至数据中心故障来测试应用的可恢复能力和监控系统。

熔断与可上线代码

熔断同舱壁和超时等模式一起出现在 Release It! 这本书中。这些模式一起实现是构建互相通信的应用的至关重要的准则。这篇 Netflix 的博客很好地解释了他们是如何应用这些模式的。

这种生产环境的自动化测试足够让大部分运维团队颤抖,因为这通常都会带来至少一周的工作量。这并不是说单体应用架构风格不能做复杂的监控设置 —— 只是以我们的经验看比较不常见。

由于服务可能在任何时候挂掉,快速检测到故障甚至,可能的话,自动恢复服务至关重要。微服务应用强调应用的实时监控,检测应用的各项指标(数据库每秒请求查询次数)以及业务相关的指标(例如每分钟接收到的订单数)。语义监控能够提供故障的及时报警,因此可以尽早使得开发团队跟进并调查原因。

这对微服务架构来说尤其重要,因为微服务倾向于使用服务编排和事件协同可能导致需要紧急处理的行为。很多行业专家赞许这种意外的紧急情况带来的价值,但事实是这种紧急情况有时也可能是件坏事。监控系统的重要性在于能够快速指出紧急情况,使得它们可以迅速被解决。

考虑同步调用的害处

当服务之间调用使用了大量同步调用时,你总会遇到故障倍乘效应。简单来说,这会导致单个组件的故障成为整个系统的故障。你面临的选择是,要么将调用异步化,要么管理好故障时间。在 www.guardian.co.uk ,新平台使用了简单的规则 —— 每个用户请求都是一个同步的调用;而在 Netflix,他们的 API 经过重新设计,将异步调用集成到平台调用网中。

单体应用可以被构建得像微服务一样透明 —— 事实上确实应该如此。不同之处在于微服务中,你必须知道另外一个进程中运行的服务是否已经断开连接。而单体应用的不同的库组件运行在相同的进程中,这种透明性相对来说没那么有用。

微服务团队希望看到每个服务都有复杂的监控和日志设施,例如显示服务状态是否在线,以及各种各样的运维和业务相关指标数据。常见的其他例子还有熔断断路器的状态,当前的吞吐率以及延时等。

演化式设计

微服务践行者,通常从演化式设计的背景而来,他们将服务的划分看作是使得应用开发者能够在不减缓改动速度的前提下控制变更的有效工具。变更控制并不总是意味着减少变更 —— 通过正确的态度和工具,我们也能对软件做出频繁、快速以及受控制的改动。

每当想将软件系统划分为组件时,你总会遇到该如何决定划分的问题 —— 我们该基于什么原则切分我们的应用?组件的关键属性是独立可替换以及可升级性[13]的概念 —— 这意味着我们寻找一种分割方式,想象将组件可以不影响其协同使用者般地重写。很多微服务团队更进一步地明确许多服务在长期来看终将被打散,而非演化。

网站 Guardian 是个起初被设计和构建成单体应用,但是最终演化到微服务方向的一个很好的例子。单体应用仍旧是网站的核心,但是他们更喜欢使用这个单体提供的 API 来将新加入的功能构建成微服务。这种方法对于一些天然临时性的功能来说很方便,例如一个处理体育活动的专门页面。网站的这类部分可以使用快速开发的语言构建出来,然后当活动结束后去除。这种由于市场营销活动而增加新微服务,并在几个月甚至几周后被丢弃的方法,我们在一个金融机构中也曾见到。

这种对可置换性的强调是一种通用模块化设计原则的一种具体情况,这种原则通过改动模式[14]驱动模块化演进。人们希望将一次需要改动的东西都聚集在同一个模块里。系统很少改变的部分应该同当前正在经历很大改动的部分处在不同的服务中。如果你发现你在不断地一起改动某两个服务,那么这可能是它们需要被合并的一种迹象。

将组件变为服务增加了将发布计划更细粒度规划的机会。单体应用的任何改动都需要重新全量编译和部署整个应用。而微服务中,你仅需要重新部署修改过的那些服务。这可以简化并加速发布的过程。缺点是你需要担心某服务的更改会影响它的消费者。传统的集成方式是通过版本管理解决这个问题,但在微服务世界中,版本管理仅是最后的选择。通过将服务设计成尽可能兼容接口供应方的改动可以避免很多的版本控制。

微服务是未来吗?

我们写这篇文章的主要目的是解释微服务原则的主要思想和原则。我们花时间做这些说明了我们显然认为微服务架构风格是种重要的思想 —— 一种值得企业应用严肃考虑的思想。我们最近使用这种风格构建了不少的系统,也了解到其他一些人使用并支持这种方式。

我们所知道的当前在这个领域带领这种风格前行的包括 Amazon、Netflix、The Guardian英国政府数字服务中心realestate.com.au、Forward 和 comparethemarket.com 等。2013年的会议比比皆是转向可归类为微服务的例子 —— 包括 Travis CI。还有很多组织多年来追求类似的技术,虽然也能归为微服务类,但是从来没有用过微服务这个名字。(通常这些被打上了 SOA 的标签 —— 但是正如我们所说的,SOA 在很多方面都和这种形式不同。[15]

尽管有这些正面的经验,我们仍然不能确定地说微服务就是软件架构发展的未来方向。虽然目前为止就我们的经历来说相比较于单体应用我们是更支持微服务的,然而我们也清醒地认识到目前还没有经过足够的时间来做出全面的验证。

sam-book-cn
我们的同事 Sam Newman 2014年花了大部分时间编写了这部记录我们构建微服务经验的书。如果你想深入这个话题,那么这应该是你的下一步。

通常来说,一种架构的决策往往要等到数年以后才能看清它带来的后果。我们也曾见过不错的团队带有一颗想模块化的愿望,构建出单体应用在几年后却开始逐渐腐化。很多人认为这种腐化不太可能出现在微服务中,因为服务间有明确的边界,且较难含混这个边界。然而,不等足够多的系统经历足够多的时间,我们还无法评判微服务有多成熟。

导致微服务架构演进糟糕的原因有很多。任何时候,组件化过程的成功与否取决于软件是否适合这些组件。找准组件的边界该划分在哪也很难。演化式设计认识到找准边界的困难,因此将易于重构作为很重要的一方面。但是当组件由服务组成时,重构带有远程通信的微服务比重构带有进程内库的组件要困难得多。跨服务边界移动代码很困难,任何接口层面的改动需要多个参与方之间的协调,需要加入向后兼容层,测试也会更复杂。

另一个问题是,如果组件的组织不是很清晰,那么你会发现你所做的一切只是将组件内部的复杂性搬运到了组件间的联系中。这不仅仅是产生了对复杂性的搬运,而且还将其移动到了一个更不明确且更难于控制的区域。除去服务间庞杂的关系时,当只看一个小的、简单的服务内部,你可能会觉得事情很简单。

最后,还有一个因素是团队的技能程度。技术好的团队更倾向于采用新技术。然而对技术好的团队能够效果不错的技术并不一定能对技术一般的团队奏效。我们看到了大量由于团队技能有限导致单体应用架构弄得一团糟的案例,但还需要时间来验证这类问题对于微服务影响如何。一个差劲的团队总会创造出差劲的系统 —— 很难说微服务将会减少还是加重这种混乱的糟糕情况。

我们听到一种有道理的论点认为一开始不应该使用微服务来构建应用。相反地,从单体应用开始,保持模块化,并当在单体应用开始成为问题时将其拆分成微服务。(然而这个建议并不完美,因为一个好的进程内接口通常都不是一个好的服务接口。)

因此,我们持谨慎的乐观态度撰写这篇文章。目前为止,见到足够多的微服务使得我们认为这种风格可能会是一条值得探索下去的路。我们也不能准确地说出这条路能走多远,但软件开发的一个挑战就是你只能在当前手中已有的信息量不完整的情况下进行决策。


出自微服务部落:https://blog.idevfun.io/martin-fowler-microservices/
本文译自:https://martinfowler.com/articles/microservices.html
原文作者:James Lewis 和 Martin Fowler
翻译:董干


  1. 2011年5月,在威尼斯附近一个软件架构师研讨会讨论了一种当时他们大都在探索的通用的架构风格,并使用了“microservice”这个词。在2012年5月,同一小组的他们决定使用“microservices(复数)”一词更为恰当。2012年3月,大概与 Fred George 同一时间,James在克拉科夫33度的“微服务——Unix风格Java”中演讲了上面一些思想作为案例学习。Netflix 的 Adrian Cockcroft 将这种方法描述为“更细粒度的SOA”,同本文提到的很多人——Joe Walnes, Dan North, Evan Botcher 和 Graham Tackley 一样,成为在互联网规模下这种风格的先行者。 ↩︎

  2. Unix 社区已经使用单体应用这个词一段时间。它在 《Unix 编程艺术》 中出现,用来描述过于庞大的系统。 ↩︎

  3. 许多面向对象的设计者,包括我们自己,使用服务对象这个术语来描述领域驱动设计中执行一段非绑定在某个实体上的显著过程。这同本文中我们使用的“服务”一词是不同的概念。由于服务这个词本身同时具有这些含义,我们只能接受这个不幸的事实。 ↩︎

  4. 我们认为具有下列属性的应用为社交建设:代码库,一组功能特性和需要整体花费资金。 ↩︎

  5. 原始论文可以在 Melvyn Conway 的网站上找到。 ↩︎

  6. 我们不禁想起了 Jim Webber 说 ESB 代表 “糟糕的意大利面盒子(Egregious Spaghetti Box)”。 ↩︎

  7. Netflix 明确声称了这种联系 —— 最近将他们的架构风格描述为细粒度的 SOA。 ↩︎

  8. 当规模到达很大的程度时,通常使用微服务的公司会向二进制通信协议迁移,例如 protobuf。使用这些协议的系统仍然符合端点智能化和管道简单化的特征 —— 但牺牲透明性换取了规模。大部分的互联网公司和企业不需要作出这种 tradeoff —— 透明性可能相对更重要。 ↩︎

  9. "YAGNI", "You Aren't Going To Need It" (你不会需要它的)是告诫人们在真的需要某个功能前不要增加这个需求的一种极限编程实践↩︎

  10. 声称单体应用是单一语言似乎有点不严谨 —— 为了构建如今的 web 系统,你可能需要了解 JavaScript 和 XHTML,CSS,所选的服务端语言,SQL 以及 ORM 方言。虽说不是严格意义上的单语言,但是你懂得这里的所指。 ↩︎

  11. Adrian Cockcroft 2013年11月在 Flowcon 上做的精彩演讲特别提到了 “开发者自助服务” 和 “开发者自己运行自己写的代码” 等字眼。 ↩︎

  12. 这里是不太严谨的说法。显然部署很多服务至更复杂的拓扑比部署一个单体应用要简单得多。幸运的是,模式减少这种复杂度 —— 投资工具链仍然是必经之路。 ↩︎

  13. 事实上,Dan North 将这种风格称之为 可替换组件架构 而非微服务。由于这似乎仅提到了这些特征的一个子集,我们选择后者的叫法。 ↩︎

  14. Kent Beck 在《模式实现》这本书中将这条作为了他的设计原则之一。 ↩︎

  15. 并且 SOA 也不是这种用法的历史根源。当世纪初 SOA 这个词出现时,我记得人们总说起 “我们已经这么搞很多年了”。一种说法认为这种风格的根源可以追溯至早期企业计算中 COBOL 程序通过数据文件的方式互相通信。另外一种说法认为微服务和 Erlang 编程语言模式相同,但是应用到了企业应用程序的语境中。 ↩︎

Show Comments

Get the latest posts delivered right to your inbox.