在上篇中我们讲到了微服务的几个架构特性,包括通过服务实现组件化、以业务功能为核心进行组织、产品而非项目、智能化端点与傻瓜式流程,在今天的微服务概念解析下篇中,我们将继续讲述微服务的特性,具体分析它的离散化治理、离散化数据管理、基础设施自动化、故障应对设计以及演进设计,并理性思考微服务作为一项新兴的技术成果,是否能够代表未来。
上篇传送门离散化治理聚合型治理的一大影响在于使得单一技术平台上出现标准化趋势。经验表明这类方案具备收缩特性——意味着各个实际问题并不能够轻松与解决方案对应起来。我们更倾向于使用正确的工具执行正确的任务,而且虽然部分整体应用程序能够发挥不同编程语言的独特优势,但这种情况并不常见。
微服务与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]。大家希望只变更其中必须变更的部分,而其它模块则继
版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/heiyeshuwu/article/details/51064686
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。