[转]微服务概念解析
“微服务架构”概念的提出已经有很长一段时间了,但在最近几年却开始频繁地出现。微服务架构是一种特定的软件应用程序设计方式——将大型软件拆分为多个独立可部署服务组合而成的套件方案。
·
微服务概念解析(上)
探讨这一全新架构术语的确切定义
“微服务架构”概念的提出已经有很长一段时间了,但在最近几年却开始频繁地出现。微服务架构是一种特定的软件应用程序设计方式——将大型软件拆分为多个独立可部署服务组合而成的套件方案。虽然这种架构风格的确切定义还存在争议,但并不妨碍其在众多企业的实际应用中被实践,并体现出了具备通用特征的业务功能、自动化部署、端点智能化以及对语言与数据的离散化控制能力。“微服务”——目前可谓早已人满为患的软件架构领域的新兴名词。虽然我们对于这种新生事物往往带着一种先入为主的蔑视与忽略态度,但经过几年的历练,我们发现这种软件构建风格正变得越来越具有吸引力。过去几年中已经有诸多企业将其引入实际项目,而至今其结果仍然相当积极,这甚至促使很多同业人士开始将微服务架构作为企业级应用程序的默认开发途径。但遗憾的是,目前仍然缺乏一套系统的概念定义,告诉我们微服务到底是如何实现这些成效的。
Docker
作为一种开源的应用容器引擎,帮助开发者将他们的应用以及依赖打包到一个可移植的容器中,便于应用的部署和扩展。而随之产生的微容器概念和微服务正好相辅相成,通过
Docker 封装的应用可以轻松运行在以扩容能力见长的云计算平台上。数人云作为专业的数据中心管理系统,提供了基于 Mesos 和
Docker 技术的企业级容器云生产环境,通过一键部署、横向扩展、持续集成等特性,助力微服务架构在企业应用环境的实践。
由于篇幅问题,本文将分上下两篇,下篇将于明天放送给大家。
简而言之,微服务架构风格[1]是一类将单一应用程序作为由众多小型服务构成之套件加以开发的方式,其中各项服务都拥有自己的进程并利用轻量化机制(通常为HTTP源API)实现通信。这些服务围绕业务功能建立而成,且凭借自动化部署机制实现独立部署。这些服务匹配一套最低限度的中央式管理机制,且各服务可通过不同编程语言编写而成并使用不同的数据存储技术。
要解释微服务风格,那么首先应当将其与整体风格进行比较:整体应用程序作为单一单元进行构建。企业级应用程序通常包含三个组成部分:一套客户端用户界面(由运行在用户设备上的浏览器中的HTML页面以及JavaScript代码构成)、一套后端数据库(将大量插入至数据库管理系统的大量表构成,通常采用关系数据库)以及一款服务器端应用程序。该服务器端应用程序将负责处理HTTP请求、执行域逻辑、对来自数据库的数据进行检索与更新,同时选定HTML视图并将其发送至浏览器端。此服务器端应用程序通常为单一的逻辑可执行文件[2]。任何针对该系统的变更都需要对该服务器端应用程序进行新版本构建与部署。
这样的整体服务器机制在构建此类系统中可谓不可或缺。我们用于处理请求的全部逻辑都运行在单一进程当中,允许大家使用语言中的基本功能以将该应用程序拆分为类、函数以及命名空间。通过这种方式,我们能够在开发人员的笔记本设备上运行并测试应用程序,同时利用一整套部署流程以确保全部变更都经过妥善测试而后被部署在生产环境当中。大家可以将大量实例运行在一套负载均衡方案之后,从而实现横向扩展能力。
这类整体应用程序当然能够切实起效,但人们却逐渐发现其中存在着诸多弊端——特别是在将大量应用程序部署在云环境当中的情况下。由于变更周期被大量集中于一处——即使仅仅指向应用程序中的一小部分,单一变更亦要求我们对应用程序整体进行重构与重新部署。随着时间推移,我们往往很难保证理想的模块化结构,这意味着本应只影响单一模块的变更往往会扩散至该模块之外。规模伸缩亦要求我们对整体应用程序进行规模调整,而非单纯为其中必要的部分进行资源扩容。
图一:整体型应用程序与微服务架构应用程序
正是这些弊端造就了如今的微服务架构风格:即以服务套件的形式构建应用程序。除了各服务能够单独进行部署与规模伸缩之外,每项服务还具备牢固的模块边界,甚至允许我们在不同的服务当中使用不同的编程语言进行代码编写。另外,各服务亦可由不同团队负责管理。
我们认为微服务风格并不算什么新鲜事物或者创新成果,其历史至少可以追溯至Unix设计时代。但我们同时亦坚信,微服务架构一直未能受到足够的重视,而其确实能够帮助大家更好地完成软件开发工作。
微服务架构之特性
我们无法给微服务架构风格出具一条确切的定义,但我们却可以根据该架构表现出的各类共同特性对其加以描述。正如各类根据共同特性做出的定义一样,并不是所有微服务架构都符合这些特性,但可以肯定的是具备这些特性的微服务架构占据大部分比例。尽管我们各部分内容的作者仅仅是相关技术社区中的活跃成员,但制作这份文档是为了对采用微服务架构的工作流程及成果做出总结,而且其中仍有相当一部分表述并非严格定义——只应作为常见情况考量。
通过服务实现组件化
长久以来,我们一直参与软件行业之内并意识到人们对利用组件整合方式构建系统的渴望——这种思路与我们在物理世界中采取的构建机制非常相似。而在过去几十年当中,我们发现已经有大量公共库渗透到多数语言平台当中并成为其坚实的组成部分。
在谈到我们所使用的组件时,大家可能会发现不同群体对组件的定义也有所区别。我们对组件做出的定义是,其属于软件中的一类单元,且具备可更替性与可升级性。
微服务架构会使用这些库,但其实现组件化的主要手段则是将软件拆分成多个服务。我们将“库”定义为与程序相对接且可通过内存内函数调用发挥作用的组件,而“服务”则为进程之外的组件,其可通过Web服务请求或者远程程序调用等方式实现通信。(这里的服务概念与多数OO程序中的服务对象概念有所区别[3])。
将服务作为组件加以使用(而非库)的一大原因在于,服务具备独立可部署能力。如果大家的应用程序[4]由单一进程中的多个库构成,那么指向任何单一组件的变更都会致使该应用程序必须进行重新部署。但如果该应用程序被拆分成多项服务,那么单一服务变更将只会致使该服务进行重新部署。虽然这并非绝对,例如某些变更会导致服务接口受到影响,但一套优秀的微服务架构旨在尽可能少地对服务协议中的服务边界及演进机制产生干扰。
将服务作为组件的另一个理由在于实现更为明确的组件接口。大多数编程语言并不具备用于定义明确发布接口的良好机制。一般来讲,其只会提供说明文档及规则以防止用户打破组件封装,但这同时亦会导致不同组件之间的耦合程度过高。利用明确的远程调用机制,服务能够轻松避免此类难题。
但以这种方式使用服务亦存在一定弊端。远程调用在资源需求方面往往远高于进程内调用,因此远程API需要采取粗粒度设计,但这亦会增加API的使用难度。如果大家需要更改不同组件间的职能分配,那么这类需求在跨越进程边界时往往不易实现。
通过粗略观察,我们往往会发现这些服务会与各运行时进程相映射——但这仅仅只是第一印象。一项服务可能由多个进程构成,且各进程始终共同进行开发与部署——这方面实例包括只由单一服务所使用的应用程序进程以及数据库。
围绕业务功能构建组织
当着眼于将单一大型应用程序拆分成多个组成部分时,管理人员通常更重视技术层,其中具体包括UI团队、服务器端逻辑团队以及数据库团队。当这些团队据此进行拆分时,即使是最简单的变更也将给项目造成跨团队协作负担,并因此导致时间与预算的双重支出。睿智的团队会对此进行优化,同时采取两害相权取其轻的办法——即强制要求逻辑存在于一切与之相对接的应用程序当中。换言之,也就是实现逻辑的普遍存在性。这正是所谓康威法则[5]的一种实际表现形式。
任何组织在设计一套系统(广义层面的系统)时,其设计成果都会直接体现该组织所使用的沟通结构。 --梅尔文·康威,1967年
图二:康威定律的实际体现
微服务方案对于各部门而言是一种不同于以往,且以业务功能为核心的服务拆分及组织途径。此类服务采用软件方案在业务层面中的广泛实现堆栈,具体包括用户界面、持久性存储以及任何外部协作机制。因此,各团队将拥有跨职能特性,包括开发过程当中要求的全部技能组合:用户体验、数据库以及项目管理等等。
图三:由团队边界决定的服务边界
微服务架构有多“微”?
尽管“微服务”早已成为一种极具人气的架构类型,但这一名称却并不能准确反映服务的实际规模——换言之,“微”服务并不一定微。在与众多微服务从业者的交流当中,我们发现服务的具体规模可谓多种多样。其中规模最大的成果源自Amazon公司旗下的“两块披萨”团队(即整个团队只需两块披萨即可填饱肚子),这意味着其总人数在十位左右。而规模较小的团队则由六人组成,负责支持六项服务。
那么这就带来了新的问题:这种十二人对单项服务的机制同一人对单项服务之间存在着怎样的差别?二者也许不可一概而论。就目前而言,我们姑且认为双方属于同类团队结构,但随着对微服务认识的持续深入,也许我们未来将抱持新的观点。
采取此类组织方式的企业实例可参见 www.comparethemarket.com ,其各职能团队共同负责构建并运营每款产品,而每款产品则被拆分为一系列独立的服务——且各服务间通过一套消息收发总线实现通信。
大型整体应用程序亦可以始终围绕业务功能实际模块化,不过这种状况并不常见。诚然,我们都听说过由大型团队构建的单一整体应用程序根据自身业务线进行设计与划分。然而在这类情况下,最大的问题在于整体应用程序在组织当中需要考虑太多背景信息。如果其整体范畴当中包含太多模块边界,那么团队中的单一成员将很难通过短期记忆对其进行管理。除此之外,我们发现这种模块化业务线的维护工作还要求相关人员具备极高的专业技能水平。相比之下,服务组件能够令拆分方式更为明确,从而大大简化团队边界的设定与认知。
产品而非项目
大部分应用程序开发工作都会遵循项目模式:其目标在于交付软件方案中的特定部分,并拥有直观的完成指标。在软件开发工作完成后,其会被传递至运维部门,这时负责构建该软件的团队也将即刻解散。
微服务的支持者们则认为这种模式并不可取——他们的主张是相关团队应该伴随产品走过整个生命周期。这方面最典型的例子应该是Amazon公司提出的“谁构建,谁运行”原则,其中开发团队需要对生产环境下的软件成果承担全部责任。这就要求开发人员在日常工作中全程关注其软件的生产运行情况,同时掌握来自用户的反馈意见,意味着他们需要在一定程度上为用户提供技术支持服务。
产品的定位应始终与业务功能相协调。相较于以往将软件视为一整套已经完成的功能集的心态,微服务架构要求我们全程与之保持关联,并思考该软件能够如何协助用户加强业务功能。
当然,我们完全可以将同样的思路引入整体应用程序当中,不过大量小型服务集合能够显著简化服务开发人员与及用户之间的个人联系。
智能化端点与傻瓜式流程
在跨越不同进程构建通信结构时,我们发现很多产品及方案会直接把智能化机制塞进通信机制本体当中。这方面的典型实例就是企业服务总线(简称ESB),ESB产品当中通常包含复杂度极高的消息跌幅、编排、转换以及业务规则应用等机制。
微服务社区则倾向于使用另一种实现方式:智能化端点与傻瓜式流程。采用微服务架构的应用程序旨在尽可能实现解耦化与关联性——它们各自拥有自己的域逻辑,而且在经典Unix场景下的运作方式更像是过滤器机制——接收请求、应用合适的逻辑并生成响应。这一切都通过简单的REST类协议实现编排,而非经由WS-Choreography或者BPEL等复杂协议以及中央编排工具实现。
目前最常用的两类协议为配合源API的HTTP请求-响应与轻量化消息收发协议[6]。对于前者,最简练而准确的说明是:
立足于Web,而非居于Web背后。 -- Ian Robinson
微服务团队采用的正是万维网(在很大程度上亦包括Unix在内)所遵循的原则与协议。一般来讲,其使用的资源能够为开发人员或者运维人员轻松实现缓存处理。
第二类作法则是立足于轻量化消息总线实现消息收发。这类基础设施选项通常具备傻瓜式特性(这种傻瓜特性体现在实现操作上,即只需匹配消息路由机制,再无其它)——以RabbitMQ或者ZeroMQ为代表的简单实现方案仅仅需要提供一套可靠的异步结构,而服务的全部智能化元素仍然存在于端点当中并负责消息的生成与消费。
在整体应用程序当中,各组件在进程内执行并通过方法调用或者函数调用的方式实现彼此通信。将整体应用程序转化为微服务形式的最大难题在于改变这种通信模式。由内存内方法调用指向PC通信机制的简单转换往往无法良好起效。相反,大家需要利用粗粒度方式取代原本的细粒度通信机制。
脚注
1: “微服务”一词最早被威尼斯附近的一个软件架构师小组于2011年5月首次提及,当时他们用这个词汇来描述自己近期研究项目当中所涉及的通用性架构机制。2012年5月,该小组作出最终决议,认为“微服务”是最适合的架构名称。2012年3月,James在《微服务-Java以及Unix方式》当中就此发表了一篇案例研究报告,而Fred George也几乎在同一时间进行了相同的工作。Netflix公司的Adrian Cockcroft将微服务架构称为“细化SOA”,并认为这是一套在Web规模下具备开创意义的架构类型。Joe Walnes、Dan North、Evan Botcher以及Graham Tackley也分别在这篇文章中对此作出了评论。
2: 文章中所使用的“整体”一词长久以来一直被Unix业界所使用。其首次出现在《Unix编程艺术》一书中,用于描述那些过于庞大的系统方案。
3: 很多面向对象设计人员,也包括我们自己,都会在域驱动设计当中使用“服务对象”这一表述,专指那些并不具备实质性联系但却拥有重要作用的对象。这与我们在本文中所使用的“服务”一词在表意上完全不同。遗憾的是,服务这个词汇同时具备两种含义,而我们对这种多义词也没有更好的处理办法。
4: 我们将一款应用程序视为一套社会性体系,其中融合了代码库、函数组以及供应主体。
5: 大家可以查看梅尔文 康韦网站上的原文论述。
6: 对于规模极为庞大的应用体系,企业通常会采用二进制协议——例如protobufs。使用二进制协议的系统仍然符合智能化端点与傻瓜式通道的特性——并为了规模化而在透明度方面作出妥协。不过大多数Web方案与绝大多数企业不需要在这方面考虑太多——一般来讲,透明度越高、效果就越好。
来源:http://dockone.io/article/1006
微服务概念解析(下)
在上篇中我们讲到了微服务的几个架构特性,包括通过服务实现组件化、以业务功能为核心进行组织、产品而非项目、智能化端点与傻瓜式流程,在今天的微服务概念解析下篇中,我们将继续讲述微服务的特性,具体分析它的离散化治理、离散化数据管理、基础设施自动化、故障应对设计以及演进设计,并理性思考微服务作为一项新兴的技术成果,是否能够代表未来。
上篇传送门
离散化治理
聚合型治理的一大影响在于使得单一技术平台上出现标准化趋势。经验表明这类方案具备收缩特性——意味着各个实际问题并不能够轻松与解决方案对应起来。我们更倾向于使用正确的工具执行正确的任务,而且虽然部分整体应用程序能够发挥不同编程语言的独特优势,但这种情况并不常见。
微服务与SOA
当我们探讨微服务时,经常出现的问题就是其到底是不是我们十年前就听说过的面向服务架构(简称SOA)的另一种表现形式?二者之间确实存在一定联系,因为微服务风格拥有与SOA相似的逻辑主张。然而问题在于,SOA的实际含义太过广泛,而且当我们提到所谓“SOA”时,实际所指的对象往往跟这里提到的微服务概念差之千里——具体来讲,其通常代表那些专注于利用ESB实现的集成化整体应用程序。
值得强调的是,我们也见证了大量表现糟糕的面向服务实现手段——从将复杂性隐藏在ESB当中[7]的作法,到投入多年以及数百万资金却毫无成效的尝试,再到以聚合型治理模式抑制变更,我们几乎看不到面向服务架构能够带来什么显著的积极影响。
诚然,微服务社区当中使用的不少技术成果都源自开发人员在大型企业当中积累到的集成化服务成果。Tolerant Reader模式正是其中的典型代表。对Web的运用确实带来可观回报,而使用简单协议正是经验积累的直接产物——这显然是为了解决标准汇聚所导致的高复杂性难题(无论何时,如果大家需要利用一种实体来管理其它实体,那么就意味着各位已经面临着大麻烦)。
SOA的这些弊端导致一部分微服务布道者很讨厌人们把SOA的标签加在微服务头上——尽管也有一些人认为微服务正是SOA的一种实现方式[8],或者说我们可以将微服务称为“面向服务的正确实现”。无论如何,事实上SOA含义的宽泛性意味着其更适合作为一种用于定义架构风格的术语,而非具体解决方案。
通过将整体应用程序的各组件拆分成服务,我们能够对各服务进行分别构建。各位可能希望利用Node.js建立一套简单报告页面?照此办理即可。打算利用C++构建特定的近实时组件?没问题。打算利用不同类型的数据库以匹配单一组件的读取行为?目前的技术方案已经能够实现这种独立重构需求。
当然,我们能够实现以上目标,并不代表我们必须这么做——但对系统进行拆分意味着大家能够拥有更多备用选项。
采用微服务架构的团队倾向于以不同的方式实现所谓标准。相较于以往编写一整套定义标准集的作法,他们更乐于开发实用工具并交付给其他开发人员,从而利用其解决自身面临的类似问题。这些工具通常能够在更为广泛的层面得到实现与共享,但同时又不至于转化为排他性内部开源模式。现在git与github都已经成为客观层面的版本控制系统选项,而开源实践也越来越多地成为内部环境中的常见组成部分。
Netflix公司就是个很好的例子,他们遵循的正是这样一种理念。将具备实用性且经过严格考验的代码作为库,并鼓励其他开发人员利用其以类似的方式解决的类似的问题,这就为各团队成员在必要时选择其它工具保留了空间。共享式库专注于数据存储、进程间通信以及我们在后文中将要探讨的基础设施自动化等问题的解决。
对于微服务社区而言,资源成本显然是种不受欢迎的因素。这并不是说该社区不承认服务协议的价值。恰恰相反,这是因为他们希望构建起大量服务协议。他们希望能够采用多种完全不同的方式对这些协议进行管理。像Tolerant Reader以及Consumer-Driven Contacts这样的模式在微服务架构中非常常见。这些服务协议也各自以独立方式不断演进。将消费者驱动型协议作为构建工作组成部分的作法能够显著增强参与者信心,同时快速获取服务功能能否确切实现的反馈意见。事实上,澳大利亚的某个团队就在积极利用消费者驱动型协议进行新服务构建。他们使用的简单工具确保其能够针对单一服务实现协议定义。其甚至在面向新服务的代码被编写出来之前就已经成为自动化构建流程中的一部分。这意味着服务只有在切实满足该协议要求的前提下才能够实现构建——这就有效解决了构建新软件时经常出现的“YAGNI”[9]难题。这些技术与工具成果围绕协议而生,并通过降低不同服务间的耦合性限制了其对中央协议管理机制的依赖。
多种语言,多种选项
JVM作为平台的快速发展已经成为多种语言混成于单一通用平台内的最新明证。这种作法已经成为一类常见实践,旨在充分发挥高级语言在过去数十年中发展所实现的种种高级抽象优势。其甚至以涓滴效应影响到裸机以及通过低级语言编写的性能敏感型代码。然而,众多整体应用程序并不需要这种级别的性能优化效果,亦非常见的DSL与高级别抽象开发成果。相反,整体应用程序往往使用单一语言,这也严重限制了其能够使用的技术手段。[10]
也许离散化治理的人气正是源自Amazon方面提出的“谁构建,谁运行”原则。各团队需要为其构建的软件的各个方面承担责任,包括为软件提供24/7全天候运维支持。这种程度的责任下放当然还没有成为常态,不过我们已经看到越来越多的企业开始将责任交付至开发团队。Netflix公司亦是另一家采取这种理念[11]的企业。为了不至于在凌晨三点被紧急来电叫醒,开发人员们当然会全力以赴提升所编写代码的质量水平。这些思路与传统的集中化治理模式明显相去甚远。
离散化数据管理
数据管理离散化拥有多种不同的表现形式。从最为抽象的级别来看,这意味着全局概念模型将在不同系统之间有所区别。这种问题常见于解决方案在大型企业当中的部署,毕竟销售团队对于客户概念的理解方式必须不同于技术支持团队的理解方式。被销售人员视为客户的对象也许根本不会出现的技术支持团队的视野当中。不同属性甚至是相同属性的不同理解方式都可能在语义层面产生细微的差异。
实践性规范与执行标准
这种态度实际有点二分法的意味:微服务团队倾向于回避由企业架构部门制定的硬性执行标准,但却乐于使用甚至积极推广HTTP、ATOM以及其它微格式开放标准。
二者之间的本质区别在于标准的开发方式以及执行方式。由IETF等组织管理的标准只会在得到广泛采用之后才能真正成为业界规范,而且其往往脱胎自成功的开源项目。
这些标准拥有与商业世界完全不同的立场与定位——事实上,商业标准的制定工作往往由那些几乎不具备编程经验的团队所负责,或者受到具体厂商的过度影响。
这一问题通常出现在不同应用程序之间甚至是应用程序之内,特别是在将应用程序拆分为多个独立组件的情况下。解决问题的一类可行思路在于基于背景边界化的区域驱动型设计(简称DDD)方案。DDD机制将一个复杂的区域拆分成多个具备边界的背景单元,并对各单元之间的关系加以映射。这种方式同时适用于整体与微服务架构,但服务与背景边界间的自然关联性有助于声明我们曾在业务功能章节中提到过的区分效果。
除了对概念模式进行离散化处理,微服务同时也能够拆分数据存储决策。尽管整体性应用程序倾向于使用单一逻辑数据库保存持久性数据,但企业通常更乐于利用单一数据库涵盖一系列应用程序——而且大多数此类决策立足于具体供应商提供的授权商业模式。微服务机制则选择由每项服务管理其自身数据库的方式,而非不同实例基于同一数据库技术或者完全使用多种不同数据库系统——这种方式亦被称为混合持久化。大家可以利用混合持久化方案打理整体应用程序,但其在微服务架构中的亮相频率明显更高一些。
对微服务架构内数据责任关系的离散化处理也影响到了更新管理工作。常见的更新处理方案是在更新多种资源时,利用事务处理机制来保证其一致性。这种方式通常被用于整体性应用程序汉中。
这种事务处理使用方式确实有助于保障一致性,但却会带来显著的临时性耦合效果,而这在跨越多项服务时会带来新的难题。分布式事务处理非常难以实现,因此微服务架构更强调服务之间的事务处理协调性,同时明确强调只需保障最终一致性并通过补偿运算解决其中的冲突问题。
利用这种方式管理一致性问题已经成为众多开发团队的新困境,但其却能够切实匹配业务实践。一般来讲,企业需要保留一定程度的不一致性以实现某种程度的逆转能力,从而利用快速响应处理错误状况。这种权衡有其必要性,只要确定失误成本要低于高一致性条件下可能造成的业务损失成本即可。
基础设施自动化
基础设施自动化技术在过去几年中得到了长足发展——而云与AWS的演进则显著降低了构建、部署及运维微服务架构所带来的复杂性水平。
大部分利用微服务机制构建的产品或者系统都是由具备丰富的持续交付及其前者——持续集成——经验的团队所完成。通过这种方式构建软件的团队能够充分发挥基础设施自动化技术成果的潜在能力。我们可以将整个流程整理成以下图表:
图五:基本构建流程
让正确决定更易于执行
作为一项连带效应,我们发现实现持续交付与部署能够帮助开发人员及运维人员创造出高实用性工具。这类工具能够创建artifact、管理代码库、建立简单服务或者实现标准监控与记录等常见功能。这方面最典型的实例当数Netflix公司发布的一系列开源工具,险些之外Dropwizard等方案亦得到广泛使用。
整体应用程序的构建、测试与推送流程能够在此类环境下顺利完成。事实证明,一旦大家利用自动化流程进行整体应用开发,那么部署更多应用程序也将成为顺理成章的轻松任务。请记住,持续交付的目标之一就是令部署变得无脑化,这意味着无论是一款应用还是三款,其实际部署流程都不会有什么区别[12]。
我们还发现,不少团队在利用这种广泛的基础设施自动化能力管理生产环境下的微服务架构。相较于前面提到的整体与微服务应用在部署层面并没有太大区别,实际运维环境下的具体条件则存在着巨大差异。
图六:模块部署的具体方式往往差别巨大
故障应对设计
将服务作为组件加以使用的结果之一在于,应用程序需要经过针对性设计以确保其具备服务故障容错能力。任何服务调用都有可能因为供应程序不可用而发生问题。在这种情况下,客户端必须要尽可能做出适当的回应。相较于整体应用程序来说,服务即组件机制会增加额外的处理复杂性,这也是微服务架构的一大弊端。在这种情况下,微服务团队需要不断审视服务故障对用户体验造成的影响。Netflix公司的“猴子军团”项目就专门负责在正常运营期间对服务进行破坏,甚至利用数据中心故障来测试应用程序的弹性及监控能力。
断路器与可交代生产环境之代码
断路器模式出现在Amazon的Release It!当中,其中提到的其它模式还包括隔板模式与超时模式等。在加以结合之后,这些模式将在构建通信应用方面发挥巨大作用。Netflix公司发布的一系列博文就很好地解释了他们对这些模式选项的具体使用方式。
这类自动化测试机制往往会令正等待周末下班的运维团队们感到不寒而慄。这并不是说整体架构风格就无法使用高复杂性监控机制——只不过这种情况确实不太常见。
由于服务随时可能发生故障,因此最重要的就是保持对故障的快速检测能力,并在可能的情况下对其进行自动恢复。微服务应用程序高度强调对应用程序的实时监控能力,同时不断对架构元素(数据库每秒钟接收到的请求数量)以及业务相关指标(例如每分钟收到的订单数量)进行记录。语义监控能够通过早期预警系统抢先一步做出警示,并引导开发团队对问题加以跟进与调查。
这一点对于微服务架构尤为重要,因为微服务更倾向于采用由编排及事件协作实现的应急处理方式。尽管很多专家都对应急处理方案偶尔带来的收益表示认同,但其实际上往往也是让事情变糟的罪魁祸首。为了及时阻断糟糕的应急处理并确保其拥有可恢复性,监控系统就变得极为重要。
同步调用殊不可取
无论何时时,一旦在不同服务之间进行多次同步调用,那么可能引发宕机的概率也会以乘法形式增长。简单来讲,系统的总体宕机时间为各单个部件宕机时间的乘积。这时我们就面临着具体选择,到底是以异步方式进行调用,还是以计划方式管理由同步调用带来的宕机时间。英国《卫报》网站在其全新平台上执行了一项简单的规则——每个用户请求对应一次同步调用,而Netflix公司所使用的API则经历重新设计,确保其结构内采用异步调用机制。
整体应用程序的构建方式可与微服务架构同样透明——事实上也本应如此。二者的区别在于,在面对整体应用时我们需要在确切了解其运行在不同进程中的服务何时发生断开。考虑到同一进程当中可能包含多套库,这种透明度水平实际上很难实现。
微服务团队需要利用复杂的监控与记录机制处理各项服务,例如通过仪表板显示上线/下线状态以及一系列运营与业务相关指标。另外,我们还需要面对断路器状态、当前数据吞吐量以及延迟等其它常见的衡量数据。
演进设计
微服务从业者通常都具备演进设计工作背景,并将服务拆分视为一种深入型工具,旨在帮助应用程序开发人员在无需拖慢变更速度的前提下实现面向应用程序的变更控制。变更控制并不一定意味着变更数量削减——配合正确的态度与工具,大家完全可以帮助软件提供快速、频繁且经过良好控制的变更。
当尝试将一套软件系统拆分为多个组件时,我们往往面临着与具体拆分工作相关的决策任务——即我们应该遵循怎样的方针对应用程序进行拆分?而组件中的关键属性则在于其独立替换与可升级特性[13]——这意味着我们要找到确切的平衡点,保证自身能够在不影响其它协作对象的前提下对单一组件进行重写。事实上,很多微服务团队会更进一步,直接清退某些服务而非对其进行长期升级。
英国《卫报》网站就是个很好的例子,其应用程序在设计与构建方面作为整体应用存在,但却在逐步面向微服务架构演进。该网站的核心部分仍然属于整体性项目,但他们更倾向于通过构建微服务利用整体API实现新功能添加。这套方案对于临时性功能的实现非常重要,例如加设专题页面以显示体育赛事报道。网站中的这类组成部分能够通过快速开发语言在短时间内编写完成,并在对应事件结束后立即下线。我们还发现其它一些金融机构亦采取类似的方式公布突发性市场波动,并在数周或者数月之后将其下线。
这也强调了可替换性在模块化设计中的重要地位,其主旨正在于将模块机制贯彻整个变更模式[14]。大家希望只变更其中必须变更的部分,而其它模块则继续保持原样。系统当中那些几乎很少变动的部分应该立足于不同于高变更频率组件的服务。如果大家发现自己经常需要同时对两项服务做出变更,那么明显应该将二者加以合并。
将组件纳入服务也让我们能够以更高的细粒度水平进行规划制定。在整体应用程序当中,任何一项变更都需要对应用整体进行重构与重新部署。但在微服务架构方面,我们只需要重新部署包含对应变更的服务。这能够显著简化并加快发布流程。不过其弊端在于,我们必须考虑针对单一服务的变更是否会影响到其它服务。传统的整体性方案能够通过版本控制解决这类难题,但微服务领域则倾向于将版本控制作为最后一种应急办法。我们可以通过设计保证服务拥有强大的容错能力,从而应对其供应程序中出现的各类代码修改。
微服务是否代表着未来?
我们撰写这篇文章的主要目的在于解释微服务架构的基本思路与原则。而在撰写过程当中,我们明确意识到微服务架构风格确实是一项值得重视的关键成果——企业级应用程序开发人员应当对其加以了解。我们最近利用该架构构建了多套系统,而且了解到亦有其它多家企业将其纳入业务体系。
我们了解到的微服务架构先驱企业包括Amazon、Netflix、英国《卫报》、英国政府数字化服务局、realestate.com.au、Forward以及comparethemarket.com等等。2013年召开的相关会议则公布了更多参与其中的重要厂商。除此之外,另有相当一部分企业一直在使用类似的实现思路——但却并没有使用‘微服务’这样的称谓。(其通常将其冠以SOA标签——不过正如我们之前提到,SOA是一类存在大量矛盾取向的概念组合。[15])
尽管拥有这些积极的经验,但我们仍然无法完全肯定微服务架构就代表着软件未来的发展方向。虽然我们的实际经历证明微服务架构截至目前仍拥有优于整体性应用程序的积极优势,但必须承认只有充分的时间积累才能帮助我们做出真正完整则准确的判断结论。
我们的同事Sam Newman曾于2014年倾尽心力撰写出这本关于我们如何构建微服务架构类应用的论著。如果大家希望进一步探讨这个议题,请千万不要错过。
通常来说,架构决策的实际影响可能需要几年之后才能逐步显现出来。我们已经看到不少优秀的团队带着巨大的热情与愿景而投入工作,但最终却构建起一套陈旧不堪的整体性架构。很多人认为同样的情况不太可能发生在微服务架构身上,因为其服务边界非常明确因此不太可能发生相互影响。但由于时间尚短且系统程度不足,我们目前还无法真正评估微服务架构的成熟度水平。
人们对微服务成熟度抱持的怀疑态度也有其理由。在任何组件化尝试工作当中,最终结果的成功与否都取决于该软件与拆分后组件的契合效果。我们目前仍然很难说明组件边界的选择原则。演进设计导致边界划分变得非常困难,因此最重要的是保证其重构的简易性。但一旦将组件作为服务处理以实现远程通信,那么其重构难度将远远高于进程内库。在不同服务边界之间进行代码移动难度极大,而任何接口变更都需要在不同相关服务间实现,同时添加层的向下兼容能力,这无疑会令测试工作更加复杂。
另一大问题在于,如果相关组件间的关系不够简洁,那么我们就相当于把组件内部的复杂性转移到了不同组件间的连接当中。这样做不仅会导致复杂性扩散,同时亦会导致其明确性缺失且难以控制。立足于小型、简单组件审视问题总是更为直观,而在不同服务间进行纵览则往往会错失关注点。
最后,团队的技能水平也将起到决定性作用。新型技术成果往往要求高水平技术团队加以实施。不过高水平团队能够顺畅利用的技术方案并不一定能够在低水平人员手中发挥作用。我们已经见证了众多低水平团队构建起的如一团乱麻般的整体架构,但仍需要时间来了解微服务架构是否会在同样的情况下引发同样的状况。诚然,糟糕的团队创建出的始终只能是糟糕的系统——但我们不知道微服务架构到底是会缓解这种状况,还是令状况更中惨不忍睹。
目前有一种较为理性的论调,认为我们不应将微服务架构作为起步方案。相反,大家可以从整体性开发风格出发,保证其结合模块化机制,并在整体性特征引发实际问题后逐步将其拆分为微服务形式。(不过这样的建议并非完全理想,因为良好的进程内接口往往并不能成为良好的服务接口。)
因此我们对此抱持谨慎的乐观态度。到目前为止,我们已经了解到关于微服务架构的方方面面,而且其应该能够成为一种极具价值的开发手段。虽然还不能做出最终判断,但软件开发工作的固有挑战之一,正是我们只能根据目前掌握的远称不上完美的信息做出决策。
脚注
7: 虽然无关紧要,但Jim Webber曾经将ESB解释成“Egregious Spaghetti Box”,也就是“恐怖意面盒”。
8: Netflix公司最近将其架构类型称为“细化SOA”。
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编程模型其实是同一回事,不过后者只被应用在企业应用程序当中。
来源:http://dockone.io/article/1007
更多推荐
已为社区贡献1条内容
所有评论(0)