过去几年来,“微服务架构”这个术语出现了,它描述了一种将软件应用程序设计为可独立部署的服务套件的特定方式。尽管这种架构风格没有确切的定义,但围绕业务能力,自动化部署,端点智能以及语言和数据的分散控制等方面存在着某些共同特征。
“微服务” - 在软件架构拥挤的街道上又一个新名词。尽管我们的自然倾向是以轻蔑的眼光来传递这样的东西,但这些术语描述了一种我们发现越来越吸引人的软件系统风格。我们已经看到许多项目在过去几年中都采用了这种风格,迄今为止的结果是积极的,因此对于我们的许多同事来说,这正成为构建企业应用程序的默认风格。可悲的是,没有太多的信息概述了微服务的风格以及如何去做。
简而言之,微服务架构是一种将单应用程序作为一套小型服务开发的方法,每种应用程序都在其自己的进程中运行,并与轻量级机制(通常是HTTP资源的API)进行通信。这些服务是围绕业务功能构建的,可以通过全自动部署机制进行独立部署。这些服务的集中化管理已经是最少的,它们可以用不同的编程语言编写,并使用不同的数据存储技术。
微服务架构
在开始介绍微服务风格(microservice style)前,比较一下整体风格(monolithic style)是很有帮助的:一个完整应用程序(monolithic application)构建成一个单独的单元。企业应用程序通常建立在三个主要部分中:一个客户端用户界面(由用户计算机上的浏览器中运行的HTML页面和JavaScript组成)数据库(包括插入常见的通常是关系数据库管理的多个表系统)和一个服务器端应用程序。服务器端应用程序将处理HTTP请求,执行特定领域逻辑,通过数据库进行检索和更新数据,选择并填充要发送到浏览器的HTML视图。这个服务器端应用程序是一个庞然大物 - 一个逻辑可执行文件[2]。系统的任何更改都涉及构建和部署新版本的服务器端应用程序。
这样的整体服务(monolithic server)是一种构建系统很自然的方式。处理请求的所有逻辑都在一个进程中运行,允许您使用语言的基本功能将应用程序划分为类,函数和名称空间。谨慎操作时,您可以在开发人员的笔记本电脑上运行和测试应用程序,并使用部署通道来确保更改经过适当测试并部署到生产环境中。您可以通过在负载平衡器后面运行多个实例来横向缩放整体。
单体式应用程序可以取得成功,但越来越多的人会感到失望 - 尤其是随着更多应用程序被部署到云中。变更周期是连在一起的 - 对应用程序的一小部分进行更改,需要重建和部署整个程序。随着时间的推移,它通常很难保持良好的模块化结构,使得难以保持应该:模块内的一个改动仅影响该模块本身中。自适应需要自适应整个应用程序,而不是它的一部分,这样做需要更多资源。
微服务架构
这些挫折引出了微服务架构风格:将应用程序构建为服务套件。除了服务是可独立部署和可伸缩的事实之外,每个服务还提供了一个严格的模块边界,甚至允许用不同的编程语言编写不同的服务。它们也可以由不同的团队来管理。
我们并不是说微服务风格是新颖的或创新的,它的根源至少可以追溯到Unix的设计原则。但我们确实认为,没有足够多的人考虑使用微服务架构,如果他们使用了,那么许多软件开发将会更好。
微服务体系结构的特征
我们不能说对微服务架构风格有一个正式的定义,但是我们可以尝试描述我们所看到的与“微服务”标签相符的架构的共同特征。与任何概述共同特征的定义一样,并不是所有的微服务架构都具有所有的特征,但是我们确实期望大多数微服务架构具有大多数特征。虽然我们的作者一直是这个相当松散的社区的活跃成员,但我们的目的是尝试描述我们在自己的工作中看到的东西,以及我们所知道的团队的类似努力。特别地,我们并没有给出一些符合要求的定义。
通过服务(Sevice)实现组件化
只要我们参与过软件行业,这就存在一种期盼:通过将组件整合在一起来构建系统,这与我们在现实世界中看待事物的方式非常相似。在过去的几十年中,我们已经见证了大部分语言平台中常见库的大量摘要所取得的巨大进步。在谈及组件时,我们遇到了对组件构成定义的难题。我们的定义是,组件是可独立更换和升级的软件单元。
微服务架构一样会用到各种库,但这种架构会把软件给拆分成各种不同的服务来实现组件化。这里我们定义两个重要的概念:库(library) 指的是链接到程序的组件,通过本地函数调用来使用库提供的功能;而服务 (service) 是进程外的组件,通过网络服务请求 (web service request) 或者远程函数调用之类的机制来使用里面的功能。注意这和很多面向对象程序里服务对象的机制是不同的 [3]。
之所以在组件化的软件里用服务,而不是库,一个主要原因就是各个服务是可以独立部署的。比如说,如果在同一个软件 [4] 里用了多个库,那么就算只是修改了其中一个,都会导致整个软件要被重新部署;相反,如果用的是服务,那只需要重新部署修改过的就可以。然而,有个问题是,当修改服务时,可能会把服务接口也给修改了,这样一来,服务的调用者和开发者就得自己私下协调了。好的微服务架构,就应该尽量避免这种问题;非要修改服务契约的话,也得循序渐进,让调用者有迹可循,不用私下协调。
使用服务作为组件的另一个后果是更显式的组件接口。大多数语言都没有很好的机制来定义显式发布的接口。通常,只有文档和规程可以防止客户机破坏组件的封装,从而导致组件之间的紧密耦合。通过使用显式的远程调用机制,服务可以更容易地避免这种情况。使用这样的服务确实有缺点。远程调用比进程内调用更昂贵,因此远程api需要粗粒度,这通常更难以使用。如果您需要更改组件之间的职责分配,那么当您跨越流程边界时,这种行为的移动就更加困难了。
我们可以观察到服务映射到运行时进程,但这只是第一次近似。服务可能包括多个进程,这些进程将始终会一起开发和部署,例如应用进程和服务所用到的数据库。
根据服务能力进行管理
当将大型应用程序拆分为不同组件时,通常的管理侧重于技术层,由技术层引领UI团队、服务器端逻辑团队和数据库团队的工作。当团队的这种生产线被隔离时,即使是简单的改变也会引起跨团队的项目耗时耗力。聪明的团队将围绕这一点进行优化——仅把逻辑强加到他们所能触及的任何方式中。换句话说,逻辑无处不在。这是Conway定律[5]的一个例子。
任何如设计系统(广义定义)的组织,必将创造出一个设计,其设计结构是组织的通信结构的副本。-- Melvyn Conway, 1967
微服务架构
在划分层面,微服务方法是不同的,分解成围绕业务能力所组织的服务。这些服务需要对该业务领域的软件进行广泛的实施,包括用户界面、持久性存储和任何外部协作。因此,团队是跨职能的,包括开发所需的全部技能:用户体验、数据库和项目管理。
微服务架构
微服务是指开发一个单个小型的但有业务功能的服务,每个服务都有自己的处理和轻量通讯机制,可以部署在单个或多个服务器上。微服务也指一种种松耦合的、有一定的有界上下文的面向服务架构。也就是说,如果每个服务都要同时修改,那么它们就不是微服务,因为它们紧耦合在一起;如果你需要掌握一个服务太多的上下文场景使用条件,那么它就是一个有上下文边界的服务,这个定义来自DDD领域驱动设计。
相对于单体架构和SOA,它的主要特点是组件化、松耦合、自治、去中心化,体现在以下几个方面:
一组小的服务
服务粒度要小,而每个服务是针对一个单一职责的业务能力的封装,专注做好一件事情。
独立部署运行和扩展
每个服务能够独立被部署并运行在一个进程内。这种运行和部署方式能够赋予系统灵活的代码组织方式和发布节奏,使得快速交付和应对变化成为可能。
独立开发和演化
技术选型灵活,不受遗留系统技术约束。合适的业务问题选择合适的技术可以独立演化。服务与服务之间采取与语言无关的API进行集成。相对单体架构,微服务架构是更面向业务创新的一种架构模式。
独立团队和自治
团队对服务的整个生命周期负责,工作在独立的上下文中,自己决策自己治理,而不需要统一的指挥中心。团队和团队之间通过松散的社区部落进行衔接。
我们可以看到整个微服务的思想就如我们现在面对信息爆炸、知识爆炸是一样的:通过解耦我们所做的事情,分而治之以减少不必要的损耗,使得整个复杂的系统和组织能够快速的应对变化。
我们为什么采用微服务呢?
"让我们的系统尽可能快地响应变化" - Rebecca Parson
让我们的系统尽可能快地去响应变化。其实几十年来我们一直在尝试解决这个问题。如果一定要在前面加个限制的话,那就是低成本的快速响应变化。上世纪90年代Kent Beck提出要拥抱变化,在同期出现了诸多轻量级开发方法(诸如 XP、Scrum);2001年敏捷宣言诞生,之后又出现了精益、看板等新的管理方式。如果说,这些是为了尽快的响应变化,在软件开发流程和实践方面提出的解决方案,那么微服务架构就是在软件技术和架构层面提出的应对之道。
微服务架构
微服务有多大?
虽然“微服务”已成为这种架构风格的流行名词,但它的名称确实导致了对服务规模的不当关注,以及关于什么构建“微”的争论。在我们与微服务从业者的对话中,我们看到了一系列规模的服务。据报道最大的规模遵循Amazon提出的Two Pizza团队理念(即整个团队可以由两个比萨饼喂饱),这意味着不超过十几个人。在规模较小的规模上,我们已经看到过如此的配置:一个六人团队将支撑六种服务。这就导致了这样一个问题:在这个大小范围内是否存在足够大的差异,即每六人所负责的服务数目和每人负责的服务数目不应该被集中在一个微服务标签下。目前我们认为将它们组合在一起会更好,但在我们进一步探索这种风格时,我们肯定会改变想法的。
跨职能团队负责构建和操作每个产品,并将每个产品分成多个单独的服务,通过消息总线进行通信。大型单块应用程序也可以围绕业务功能进行模块化,尽管这不是常见的情况。当然,我们会敦促一个庞大的团队构建一个单一的应用程序,以将自己与业务线分开。我们在这里看到的主要问题是,他们倾向于在太多的背景下组织。如果这个庞然大物跨越了许多模块化的边界,那么团队的单个成员就很难将它们融入到他们的短期记忆中。此外,我们发现模块化的代码行需要大量的规程来执行。服务组件所要求的更加明确的分离使得保持团队界限变得更加容易。
产品不是项目
大多我们看到的应用开发都使用一个项目模型:目标就是将那些马上就要完成的软件的一部分交付出去。在软件的完成时将它交付给一个后期运维组织,然后开发它的软件项目组就被解散了。微服务倡导尽量避免这种模型,反而更倾向于另一个理念:一个团队应该在一个产品的整个生命周期都拥有它。与之相同的灵感来自于亚马逊的“你创造它,你运维它”的理念,在那里一个开发团队对产品中的软件是完全负责的。这给开发者们带来了日常联系,让他们知道他们的软件在产品中表现如何,同时由于他们必须要承担一些支持负担,也增加了他们与用户的联系。
产品心态,与业务能力挂钩。与其将软件看作是一组要完成的功能,还存在一种持续的关系,其中的问题是软件如何帮助其用户提高业务能力。我们没有理由不使用单一的应用程序,但是服务的粒度越小,就越容易创建服务开发人员和用户之间的个人关系。
智能端点和哑管道。
在构建不同流程之间的通信结构时,我们已经看到了许多产品和方法,它们强调将大量的智慧投入到通信机制本身中。一个很好的例子是企业服务总线(Enterprise Service Bus, ESB), ESB产品通常包括用于消息路由、编排、转换和应用业务规则的复杂设施。
微服务和SOA
当我们已经讨论了微服务之后,一个常见问题为:它是不是就是我们在十年前见到的面向服务的体系架构(SOA,Service Oriented Architecture)。因为微服务风格与SOA所支持的一些主张非常像,这一点是有价值的。然而问题就是SOA意味着太多不同的东西,而且一般因为对ESB在用于集成大型应用时的关注,当大多我们遇到名为“SOA”的东西的时候,它与我们在本文所描述的风格完全不同。尤其我们已经看到了很多面向服务的拙劣的实践——从在ESB[6]中总是将复杂隐藏起来的趋势,到失败的花费数百万而没有价值的多年计划,再到总是抑制变化的集中管理模式,导致有时很难看到过去的这些问题。
当然,微服务社区中许多在用的技术都是由大型组织开发者的集成服务经验发展而来。读者容错模式就是其中的一个例子。web的使用已经为此做出了贡献,使用简单协议是另一种方法,它就是由这些经验衍生出来的——一种远离中心标准的反应,这些标准已经达到了它如今所具有的一种复杂度,直白的说,相当壮观。(当你需要一个实体来管理你的众多实体的时候,你就知道你遇到大麻烦了。)与SOA的共同表现形式已经让微服务主张彻底拒绝被打上SOA的标签,尽管其他人认为微服务就是SOA的一种形式[7],也许在面向服务方面是没有错的。无论哪种方式,SOA都意味着不同的东西,这意味着使用一个术语来更加简明的定义这种架构风格是有必要的。
微服务社区相对更倾向于另一种方法:智能终端和无声管道。使用微服务搭建的应用旨在尽可能的分解和凝聚——他们拥有他们自己的业务逻辑,而且更像一个传统Unix印象中的过滤器——接收请求,应用合适的逻辑,并产生响应。它们使用简单REST协议而非复杂协议,就像WS-Choreography或者BPEL或者使用中央工具配置。
这两种协议使用的比较多的是使用源API和轻量级消息的HTTP请求-响应[8]。第一个最好的表达是
属于web,而不落后于web --Ian Robinson
微服务团队使用万维网(以及很大程度上,Unix)构建的原则和协议。开发者或操作人员可以通过较少的努力来缓存经常使用的资源。
第二种常用方法是通过轻量级总线传递消息。选择的基础设施素来愚蠢(愚蠢是因为仅作为消息路由器)——像RabbitMQ或ZeroMQ一样不仅仅是提供可靠的异步架构来简单实现——智能仍然存在于生产和消费信息的终点上;在服务中。
在整体结构中,正在执行中的组件通过方法调用或函数调用进行通信。将一个巨大的框架改成一个微服务框架时遇到的最大的问题在于改变通信方式。从内存的方法调用到RPC的简单转换会使通信性能变差。相反,你需要用粗粒度方法来替换细粒度的通信。
分散治理
集中化治理的后果之一是在单一技术平台上进行标准化的趋势。经验表明,这种方法是压缩的——不是每个问题都是钉子,不是每个解决方案都是锤子。我们更喜欢使用合适的工具来做这个工作,而单一的应用程序可以在一定程度上利用不同的语言,这并不常见。将monolith的组件拆分为服务,我们在构建它们的时候有一个选择。要使用节点。js到standup一个简单的报告页面?就去做吧。一个特别接近实时的组件的c++ ?很好。您想要换一种不同的数据库风格,以便更好地适应一个组件的读取行为?我们有技术来重建他。
当然,仅仅因为你可以做点什么,并不意味着你应该这样做 - 但是用这种方式划分你的系统意味着你有可选项。构建微服务的团队也更喜欢采用不同的标准方法。相对于使用一套写在纸上定义好的标准,他们更偏向于这种想法:生产有用的工具,而其他开发人员可以用此工具来解决他们所面临的类似问题。这些工具通常是从实施中获得的,并在更广泛的群体中共享,有时但不完全使用内部开源模型。既然git和github已经成为了可选的事实上的版本控制系统,开源实践正变得越来越普遍。
Netflix组织就是遵循这一理念的一个很好的例子。 贡献出不仅实用,更重要的是经过实践检验的代码来作为库,来激励其他开发者以类似的方式解决类似的问题。如果需要,还留以余地去选择应用不同的方法。 共享库倾向于关注常见的问题,包括数据存储,进程间通信以及我们将进一步讨论的架构自动化。
对于微服务社区来说,虚耗并不是特别受人关注。但这并不意味着社区不重视服务契约。恰恰相反,往往有更多的人在关注。只不过他们在寻求不同的方式来管理这些契约。例如Tolerant Reader(容错读取)和Consumer-Driven Contracts(消费驱动契约)模式就通常应用于微服务。这些都会帮助服务契约去独立演进。应用消费驱动契约作为你构建的一部分会提升信心,并对你的服务是否正常运行提供快速反馈。事实上据我们所知,澳大利亚的一支团队正在推动以消费驱动契约为导向来搭建新服务。他们使用简单的能够允许他们自己定义服务契约的工具。在新服务代码被创建之前,这就变成了自动构建的一部分。这样构建出来的服务恰好满足了契约 – 这是一种在创建新软件时避免“YAGNI”[9]难题的很得当方法。这些围绕着它们发展而来的技术和工具会通过减少服务之间的时间耦合,以此来限制对中央契约管理的需要。
多语言,多选项
JVM作为平台的发展仅仅是一个最新的在通用平台上实现多语言的例子。数十年来,为了利用更高层次抽象的优势,更高级的语言已经被普遍采用。正如在更底层的水平上,降低到硬件层面去编写性能敏感的代码一样。然而,许多大型系统并不需要这个层面的性能优化,也不需要DSL和更高层面的抽象(这让我们感到非常沮丧)。相反,大型系统通常是单一语言,并且倾向于限制使用的技术数量[10]。
也许去中心化管理的最高境界就是被Amazon普及推广的构建/运行精神。团队的职责是构建软件的各个方面,包括7*24全天候运维。虽然这种水平的责任要求转变绝对不是规范,但我们看到越来越多的公司将责任交给开发团队。 Netflix是另一家采用这种风格的组织[11]。每天凌晨3点被传呼机吵醒无疑是一种强大的动力,逼迫你在编写代码时更加注重代码质量。这些创意点都与传统的集中化管理模式渐行渐远。
去中心化的数据管理
数据管理的去中心化有许多不同的方式。在最抽象的层次上,这意味着世界的概念模型将在系统之间有所不同。在跨大型企业集成时,这是一个常见问题,客户的销售视图与支持视图不同。在销售视图中,一些被称为客户的东西可能根本不会出现在支持视图中。那些有可能具有不同属性和(更糟糕)的共同属性,其语义略有不同。
经过战斗考验和执行的标准
微服务团队倾向于避开企业架构组所制定的严格执行标准,但他们乐于使用,甚至推广使用开放标准,如 HTTP、ATOM 和其他微格式,这是一个两分法。
除了关于概念模型的去中心化决策之外,微服务还分散了数据存储的决策。尽管单体式应用程序在存储持久性数据时更偏向单一的逻辑数据库,但企业通常更倾向于在各种应用程序中使用单一数据库 —— 这些决策中的很多都是由供应商的与许可授权相关的商业模式进行驱动的。微服务更偏向于让每个服务管理自己的数据库,或是相同数据库技术的不同实例,或是完全不同的数据库系统 —— 一种叫做 Polyglot Persistence 的方法。你可以在单体应用中使用 polyglot persistence,但在微服务中它使用得更频繁。
微服务架构
在微服务中对数据责任去中心化对于管理更新有一定影响。处理更新的通用方法是在更新多个资源时使用事务来保证一致性。这种方法经常在单体式应用中使用。
使用像这样的事务有助于保持一致性,但是会增加显着的时间耦合,这是在多个服务中都有问题的。分布式事务非常难以实现,因此微服务架构强调服务之间的无事务性协调,并明确认识到:一致性可能只是最终的一致性,而问题则通过补充操作来处理。选择以这种方式来管理不一致性对于许多开发团队来说是一个新的挑战,但它往往符合业务惯例。通常业务容忍一定程度的不一致性,以便快速响应需求,同时采取某种逆过程来处理错误。只要修正错误的成本低于业务失败所带来的损失,这种折中是值得的。
基础设施自动化
基础设施自动化技术在过去几年中发生了巨大变化 - 特别是云和AWS的发展降低了构建、部署和运行微服务的操作复杂性。由微服务构建的许多产品或系统都是由具有持续交付及其先驱(持续集成)方面有丰富经验的团队所构建的。以这种方式构建软件的团队广泛使用基础设施自动化技术。这在下面所展示的构建管道中进行了说明。
微服务架构
鉴于这不是一篇关于持续交付的文章,我们将仅在此提请注意几个关键功能。我们希望尽可能多的信心使我们的软件能够工作,所以我们进行了大量的自动化测试。推动可工作的软件“上”流水线意味着我们可以自动部署到每个新环境中。
轻松做正确的事情
我们发现由持续交付和部署而导致自动化程度提高的一个副作用是创建有用的工具来帮助开发人员和操作人员。用于创建工件、管理代码库、创建简单服务或添加标准监测和日志记录的工具现在很常见。网络上最好的例子可能是Netflix的Netflix开源工具系列,但也有其他的包括我们广泛使用的Dropwizard。
一个单体式应用程序将通过这些环境非常愉快地构建、测试、推动。事实证明,一旦你对单体式应用的生产路径做了投入,那么部署更多的应用程序似乎不再那么可怕。请记住,CD 的目标之一就是让部署变得枯燥,所以无论它是一个还是多个应用程序,只要它仍然枯燥,那就是并不重要的[12]。
我们看到团队频繁使用的基础设施自动化的另一个领域是管理生产环境中的微服务。与我们之前的断言相反,只要部署是很枯燥的,单体式和微服务之间没有太大的区别,每个部门的运营环境可能会有惊人的不同。
微服务架构
图 6:模块部分通常有区别
为失败而设计
使用服务作为组件的结果是,应用程序需要被设计,以便它们能够容忍服务的失败。任何服务调用都可能由于供应商的无法使用而失败,必须尽可能为客户优雅地响应。与单块设计相比,这是一个缺点,因为它引入了额外的复杂性来处理它。其结果是,微服务团队不断地反思服务失败如何影响用户体验。Netflix的Simian Army在工作日中引入了服务的失败,甚至是数据中心,以测试应用程序的弹性和监控。
断路器及生存环境预备码
断路器在 释放(Release It!,备注书名)一书中与其他如Bulkhead和Timeout等模式一起用于构建通讯软件是至关重要。这点Netflix博客 文章做了大量的解释。
这种在生产中的自动化测试将足以让大多数运营团队不寒而栗。这并不是说单一架构不具备复杂的监控设置—— 只是经验中并不常见。
由于服务可能随时发生故障,因此能够快速检测故障并在可能的情况下自动恢复服务很重要。 微服务应用程序非常重视应用程序的实时监控,检查架构元素(数据库每秒获得多少请求)和业务相关指标(例如每分钟收到多少订单)。 语义监控可以提供一个预警系统,从而引导开发团队进行跟踪和调查。
这对微服务架构尤其重要,因为微服务对编排和事件协作的偏好会导致出现紧急行为。 尽管许多权威人士称赞偶然事件出现的价值,但事实是,紧急行为有时可能是一件坏事。 监控对迅速发现不良紧急行为至关重要,只有发现才可能进行修复。
同步调用是有害的
每当您在服务之间有多个同步调用时,您就会遇到停机的乘法效应。简单地说,就是当您系统停机时间成为单个组件的停机时间时。您面临一个选择,使您的调用变成异步或管理停机时间。在www.guardian.co.uk上,他们已经在新平台上实现了一个简单的规则——每个用户在Netflix上的一个同步呼叫,他们的平台重新设计的API已经在API结构中建立了异步性。我们可以构建一个像微服务一样透明的单体——事实上,它们应该是这样的。不同之处在于,您绝对需要知道在不同进程中运行的服务何时断开。在同一过程中使用库,这种透明性就不太可能有用了。
微服务团队希望看到针对每个服务的精密的监控和日志记录设置,例如面板显示增加/停止状态以及各种与运营和业务相关的指标。有关线路断路器的状态、当前吞吐量和延迟的详细信息是我们经常遇到的其他示例。
Evolutionary Design 演进式设计
微服务从业者通常拥有进化设计背景,并将服务分解视为下一步的工具,以使应用程序开发人员能够控制其应用程序中的更改,并且不会降低变更速度。变更控制并不一定意味着变更减少 - 通过正确的态度和工具,你可以对软件进行频繁、快速且控制良好的变更。
无论您何时试图将软件系统分解为组件,您都面临着如何分割这些组件的决定 - 我们决定切割应用程序的原则是什么?组件的关键属性是独立替换和可升级性的概念[13] - 这意味着我们可以寻找重写组件而不影响其协作者的点。事实上许多微服务群都明确预计许多服务将被废弃,而不是长远发展。
卫报网站就是一个很好的例子,它是作为一个整体设计和构建的应用程序,但它一直在微服务方面发展。网站的核心仍然是个庞然大物,但他们更喜欢通过构建使用庞然大物的 API 的微服务来添加新功能。这种方法对于固有临时性的功能特别有用,例如,专门处理体育赛事的页面。使用快速开发语言可以将网站的这一部分快速组合起来,并在活动结束后将其移除。我们在一家金融机构看到了类似的方法,即在市场中存在机会的时候添加新服务并在几个月甚至几周后丢弃它们。
强调可替换性是模块化设计中的一般原则的特别情况,其原则是为了在整个模式变化中驱动模块化 [14]。你可以在相同模块并且相同时间内做修改。系统修改的那部分应很少出现在不同且相互依赖的服务中。如总是两个服务一并修改,那说明你需要要合并服务了。
将组件并于服务使得发布计划具有更大的颗粒度。单一服务下,任何修改都需重新发布整个应用,而微服务架构的情况下,只需要重新发布修改的服务,所以微服务能简化并加快发布流程。但缺点是需要担心修改某个服务使得其消费者中断。传统整合的方案是尽量使用版本来解决这个问题,但微服务偏好使用只是将版本作为下策。将服务设计得尽可能适应修改,也可以避免许多版本。
微服务未来?
我们写这篇文章的主要目的是解释微服务的主要思想和原则。通过花这么多时间,我们清楚的认为,微服务架构风格是一个重要的思想——一个为企业应用认真思考的思想。我们最近使用这个风格构建了几个系统,并认识了其他一些使用和支持这种风格的人。
我们知道有人在某种程度上开创了这种架构风格,包括亚马逊、Netflix、卫报、英国政府数字服务、realestate.com.au、Forwardh和comparethemarket.com。2013年的巡回会议上充斥着公司的例子,这些公司正在转向微服务类型——包括Travis CI。此外,有很多组织长期以来一直在做我们所说的微服务,但没有使用这个名字。(通常标记为SOA——尽管如此,SOA有许多矛盾的形式。[15])
尽管有这些积极的体验,但我们并不认为我们可以确信微服务是软件架构的未来方向。虽然迄今为止,相对于单体式应用程序,我们的体验是积极的,但我们意识到一个事实:要做出充分的判断所过去的时间并不是很充足。
微服务架构
我们的同事Sam Newman在2014年的大部分时间里写了一本关于构建微服务并记录我们体验的书。如果你想深入探讨这个话题,这应该是你的下一个目标。
通常你做的架构决策只会在几年之后才真正显现出效果。我们看到过一个项目,项目拥有一个好的团队,并强烈渴望模块化,但构建出来的巨大架构,在这些年已经衰败了。一些人相信微服务不会出现这种衰败现象,因为服务界限是明确的并且很难修复。然而,直到我们看到了足够多足够老的系统,我们仍无法正确的评价微服务框架怎样算成熟。
有人期望微服务的成熟度不佳是有原因的。在组件化的任何努力中,成功需要依赖软件如何很好的适应组件。很难确切的指出组件的边界在哪。进化设计意识到获取正确的边界是困难的,因此能轻易的重构他们是重要的。但是,当你的组件通过远程通信来服务时,重构起来比在进程中的库更困难。通过服务边界移动代码是困难的,参与者之间需要协调每一个接口变化,需要添加向后兼容层,并且测试会变得更加复杂。
如果组件不能干净编排那将引起其他问题,这时你所做的所有事情就是转移复杂度,将组件内部的连接转移到组件之间。这不仅仅是移动复杂度,转移的地方将更不清晰,且更难控制。当你查看一个简单的小组件内部时,你会很容易想到事情是好的,而忽略了服务之间的混乱连接。最后,还有团队技能的因素。新技术往往会被更熟练的团队所采用。但是对于一个更熟练的团队来说,一种更有效的技术不一定对那些不太熟练的团队起作用。我们已看到很多不太熟练的团队构建混乱的单一架构的案例,但是,当这种情况发生在微服务中时,需要时间来看看会发生什么。糟糕的团队总会创建一个差劲的系统——但很难判断微服务是否能减少了这种情况下的混乱或是否能使情况变得更糟。
我们听到的一个合理的论点是你不应该从微服务架构开始,而应从单一(庞大)的项目开始,一旦这一项目遇到问题,就拆分模块,划分不同的微服务。(虽然这个建议并不理想,因为一个好的进程接口通常不是一个好的服务接口。)所以我谨慎乐观地写下了这篇文章。到目前为止,我们已看到足够多文章认为微服务是一条值得走的路。我不能确定最终如何,但这是软件开发的一个挑战——只能根据你目前必须掌握的不完美的信息做出决定。
简法一:为什么不把这坨该死的代码拆开?每当代码打包发布的时候,一个上百兆的部署文件让我深感忧虑。我的担忧并非空穴来风,一次又一次的瓶颈让我验证了这该死的担忧。面对这样一个庞然怪物,就算无数个“通宵”也削减不了我对它的力不从心,“分解”成为了我当时的唯一想法。因为“分解”是我们人类处理复杂的一种常识化手段,它能让我把一条复杂的数学题逐一破解,也能让我把一项艰巨的任务分而治之,更让我看到了人类从“自给自足”到“专业化分工”的魅力。
专业化分工
简法二:除了MVC,我还能如何选择?无可否认,MVC是互联网时代的“王者荣耀”,但随着移动互联网的发展,我试图寻找另一种更适合多端消费的服务化抽象模式。如果我只是单纯地把沉重的SSH切换到当时较为流行且轻量的SSM,其实并没有太多本质上的区别。我们当时的这种“服务化”分解其实更多地想给“消费者”提供一种轻量化、标准化、抽象化的服务支撑,如果要用专业术语来形容,可能SOA(面向服务架构)更为贴切。但ESB和WS作为当时SOA的主流实现和工具,它们的“沉重”让我望而却之。
服务化分解
简法三:继续找合适的“轮子”还是“自造”轮子?有想法对于一个年轻人来说再正常不过,但把能把这想法付诸实现还是需要一定的付出、勇气和机遇。才疏学浅的我在当时并未看到“服务化”的普及,选择一款成熟且契合自己思路的工具也不是一件容易的事,又或许是我内心当时那份热血澎湃的重构欲望在日益膨胀。幸运的是,开放的平台给了我足够开放的心态、空间和信心去打造属于我们的“轮子”。
欲望驱动
简法四:轮子的思考“简单”是我们设计的首要原则,因为简单赋予了灵活,提高了效率、增强了可控,而且自主研发的约束范围也会远远大于工具的选择,为“简化”创造了无限可能。开源工具的思考边界可能更多地会集中在技术引擎和技术规范二者之间,因为它必须抽象在应用场景之下才能达到一定的通用性,所以开源的考虑会非常周到且功能齐全,但会存在一定的“臃肿”和“个性化”局限,这也是我们“自造轮子”的重要考虑之一,但更重要的还是从本质出发。
系统边界思考
简法五:技术引擎的思考“服务化”的设计理念会把应用根据“领域边界”分解成一个个独立的“服务进程”。其实,划分后的应用系统跟操作系统还是有几分相似之处,服务好比进程,线程好比服务的业务执行单元。事实上,它们在运行过程中就是这样一种上下层的对应关系。
服务与进程
线程的执行是基于栈帧的“跳进跳出”,而业务的执行是基于“流程”的线性执行。“流程”是业务执行的线性抽象,对“流程”的分解、定义、组织和管控恰恰就是我们对“技术引擎”设计的关键所在。
技术引擎流程抽象过程
简法六:技术引擎的实现对流程的抽象并非想说明我们“轮子”的独特之处,而是尝试对流程本质进行重新理解。因为本质,所以无论SSH还是SSM都能作为该抽象流程的一种实现。但是,我们要做的是尝试重新透过现象看本质,然后基于本质之上一砖一瓦重新打造出我们“服务化”理念的另一种实现。
系统层次分解思考
一张白纸的背后可能隐藏着数十道工序的运作,我们“轮子”每砖瓦的堆砌同样少不了对系统从始至终、由里到外的无数次观察和思考。每一次的重构都千差万别,每一次的放弃都异常挣扎,但每一次都更接近于自己的内心。
服务技术引擎结构
除了服务交互协议层外(Service Interaction protocal),我们把框架总体划分为框架服务(Framework Service)、基础服务(Base Service)和业务服务(Business Service)三个层次,各层次服务都是由定时器模块组件(Timer)、初始化模块组件(Initor)、销毁模块组件(Destoryer)、业务前置模块组件( SB_Module)、业务后置模块组件(SA_Module)以及业务实现模块组件(Services)组成。从结构上看,每一层的服务都内嵌于它上一层的服务之中,让各种模块组件形成了一种高约定、标准化、插拔式的切面规则。
微服务是什么?快速理解微服务架构 |