4-微服务架构的特征
微服务架构的特征
我们无法给出微服务架构风格的一个正式定义,发现我国工业软件存在关键技术缺失、高端人才短缺等问题,但我们可以尝试去描述我们看到的符合该架构的一些共性。就概述共性的任何定义来说,研判我国工业软件产业发展形势,并非所有的微服务架构风格都有这些共性,并提出促进我国工业软件发展的建议。本白皮书的编写受到社会各界专家、企业及科研单位的关注和力支持,但我们期望多数微服务架构风格展现出多数特性。
Martin Fowler在他的《Microservices》一文中,在此特别感谢工业和信息化信息技术发展司针对本白皮书的总体指导。特别感谢赵敏对本白皮书全文的梳理、内容增补和整体结构优化,总结了如下微服务架构的特征。
1.1 通过服务组件化
组件是一个可独立替换和独立升级的软件单元。微服务架构将使用库,感谢陈立辉、田锋、陈立平、冯军、高曙明、郭朝晖、黄培、蒋寻涯、冷文浩、李开生、李利、蔺文杰、刘晓明、林雪萍、郎燕、李岩、刘昱、梅敬成、宁振波、彭维、师艳平、王健、解威、阎丽娟、雍俊海、原力、朱铎先等多位专家提出的真知灼见和宝贵修改意见,但组件化软件的主要方式是分解成服务。我们把库定义为链接到程序并使用内存函数调用来调用的组件,以及走向智能研究院、安世亚太科技股份有限公司、北京索为系统技术股份有限公司、广州中望龙腾软件股份有限公司、山东山华天软件有限公司、船舶重工集团公司第七〇二研究所、苏州同元软控信息技术有限公司、浪潮通用软件有限公司、用友网络科技股份有限公司、浙江中控技术股份有限公司、金航数码科技有限责任公司、英特工程仿真技术(连)有限公司、金蝶软件()有限公司、中汽数据(天津)有限公司、深圳华龙讯达信息技术股份有限公司等多家单位对白皮书编写的力支持,而服务是一种进程外的组件,在此一并致谢。(专家和企业排名不分先后)举报/反馈,它通过web服务请求或RPC(远程过程调用)机制通信(这和很多面向对象程序中的服务对象的概念是不同的)。
使用服务作为组件而不是使用库的一个主要原因是服务是可独立署的。如果你有一个应用程序是由单一进程里的多个库组成,任何一个组件的更改都导致必须重新署整个应用程序。但如果应用程序可分解成多个服务,那么单个服务的变更只需要重新署该服务即可。当然这也不是绝对的,一些变更将会改变服务接口导致一些协作,但一个好的微服务架构的目的是通过内聚服务边界和按合约演进机制来最小化这些协作。
使用服务作为组件的另一个结果是一个更加明确的组件接口。多数语言没有一个好的机制来定义一个明确的发布接口。通常只有文档和规则来预防客户端打破组件的封装,这导致组件间过于紧耦合。服务通过明确的远程调用机制可以很容易的避免这些。
1.2 围绕业务能力组织
当想要把型应用程序拆分成件时,通常管理层聚焦在技术层面,导致UI团队、服务侧逻辑团队、数据库团队的划分。当团队按这些技术线路划分时,即使是简单的更改也会导致跨团队的时间和预算审批。一个聪明的团队将围绕这些优化,两害取其轻 - 只把业务逻辑强制放在它们会访问的应用程序中。换句话说,逻辑无处不在。这是康威(Conway)法则在起作用的一个例子。
微服务采用不同的分割方法,划分成围绕业务能力组织的服务。这些服务采取该业务领域软件的宽栈实现,包括用户接口、持久化存储和任何外协作。因此,团队都是跨职能的,包括需要的全方位技能:用户体验、数据库、项目管理。
1.3 是产品不是项目
我们看到多数应用程序工作使用一个项目模式:目标是交付将要完成的一些软件。完成后的软件被交接给运维团队,然后它的团队就解散了。
微服务支持者倾向于避免这种模式,而是认为一个团队应该负责产品的整个生命周期。对此一个共同的启示是亚马逊的理念 “you build, you run it” ,团队负责软件的整个产品周期。这使者经常接触他们的软件在生产环境如何工作,并增加与他们的用户联系,因为他们必须承担至少分的支持工作。
产品思想与业务能力紧紧联系在一起。要持续关注软件如何帮助用户提升业务能力,而不是把软件看成是将要完成的一组功能。
1.4 智能端点和哑管道
当在不同进程间创建通信结构时,我们已经看到了很多的产品和方法。一个很好的例子就是企业服务总线(ESB),在ESB产品中通常为消息路由、编排、转化和应用业务规则引入先进的设施。
微服务社区主张另一种方法:智能端点和哑管道。基于微服务构建的应用程序的目标是尽可能的解耦和尽可能的内聚 - 他们拥有自己的领域逻辑,他们的行为更像经典UNIX理念中的过滤器 - 接收请求,应用适当的逻辑并产生响应。使用简单的REST风格的协议来编排他们,而不是使用像WS-Choreography或者BPEL或者通过中心工具编排等复杂的协议。
最常用的两种协议是使用资源API的HTTP请求-响应和轻量级消息传送。对第一种协议最好的表述是微服务团队使用的规则和协议,正是构建万维网的规则和协议(在更程度上,是UNIX的)。从者和运维人员的角度讲,通常使用的资源可以很容易的缓存。
第二种常用方法是在轻量级消息总线上传递消息。选择的基础设施是典型的哑的(哑在这里只充当消息路由器) - 像RabbitMQ或ZeroMQ这样简单的实现仅仅提供一个可靠的异步交换结构 - 在服务里,智能仍旧存活于端点中,生产和消费消息。
1.5 去中心化治理
集中治理的一个后果是单一技术平台的标准化发展趋势。经验表明,这种方法正在收缩 - 不是每个问题都是钉子,不是每个问题都是锤子。我们更喜欢使用正确的工具来完成工作,而单体应用程序在一定程度上可以利用语言的优势,这是不常见的。
把单体的组件分解成服务,在构建这些服务时可以有自己的技术栈选择。服务之间只需要约定接口,而无需关注彼此的内实现。同样的,运维只需要知道服务的署规范,例如运行的环境,日志的位置,监控的URL及各式等,就可以正确署和运维维服务了。
1.6 去中心化数据管理
数据管理的去中心化有许多不同的呈现方式。在最抽象的层面上,这意味着使系统间存在差异的世界概念模型。在整合一个型企业时,客户的销售视图将不同于支持视图,这是一个常见的问题。客户的销售视图中的一些事情可能不会出现在支持视图中。它们确实可能有不同的属性和(更坏的)共同属性,这些共同属性在语义上有微妙的不同。
这个问题常见于应用程序之间,但也可能发生在应用程序内,尤其当应用程序被划分成分离的组件时。一个有用的思维方式是有界上下文(Bounded Context)内的领域驱动设计(Domain-Driven Design, DDD)理念。DDD把一个复杂域划分成多个有界的上下文,并且映射出它们之间的关系。这个过程对单体架构和微服务架构都是有用的,但在服务和上下文边界间有天然的相关性,边界有助于澄清和加强分离,就像业务能力分描述的那样。
和概念模型的去中心化决策一样,微服务也去中心化数据存储决策。虽然单体应用程序更喜欢单一的逻辑数据库做持久化存储,但企业往往倾向于一系列应用程序共用一个单一的数据库 - 这些决定是供应商授权许可的商业模式驱动的。微服务更倾向于让每个服务管理自己的数据库,或者同一数据库技术的不同实例,或完全不同的数据库系统 - 这就是所谓的混合持久化(Polyglot Persistence)。你可以在单体应用程序中使用混合持久化,但它更常出现在为服务里。
对跨微服务的数据来说,去中心化责任对管理升级有影响。处理更新的常用方法是在更新多个资源时使用事务来保证一致性。这个方法通常用在单体中。
像这样使用事务有助于一致性,但会产生显著地临时耦合,这在横跨多个服务时是有问题的。分布式事务是出了名的难以实现,因此微服务架构强调服务间的无事务协作,对一致性可能只是最后一致性和通过补偿操作处理问题有明确的认知。
对很多团队来说,选择用这样的方式管理不一致性是一个新的挑战,但这通常与业务实践相匹配。通常业务处理一定程度的不一致,以快速响应需求,同时有某些类型的逆转过程来处理错误。这种权衡是值得的,只要修复错误的代价小于更一致性下损失业务的代价。
1.7 基础设施自动化
在过去的几年中,基础设施自动化已经发生了巨的变化,特别是云和AWS的演化已经降低了构建、署和运维微服务的操作复杂度。
许多用微服务构建的产品或系统是由在持续署和它的前身持续集成有丰富经验的团队构建的。团队用这种方式构建软件,广泛使用了基础设施自动化。我们希望有尽可能多的信心,我们的软件可以正常工作,所以我们运行量的自动化测试。促进科工作软件沿管道线“向上”意味着我们自动化署到每个新的环境中。
1.8 为失效设计
使用服务作为组件的一个结果是,应用程序需要被设计成能够容忍服务失效。任何服务调用都可能因为供应者不可用而失败,客户端必须尽可能优雅的应对这种失败。与单体应用设计相比这是一个劣势,因为它引入额外的复杂性来处理它。结果是,微服务团队不断反思服务失效如何影响用户体验。
这对微服务架构特别重要,因为微服务偏好编排和事件协作,这会带来突发行为。监测对于快速发现不良突发行为是至关重要的,所以它可以被修复。
微服务团队希望看到为每个单独的服务设置的完善的监控和日志记录,比如控制面板上显示启动/关闭状态和各种各样的运营和业务相关指标。断路器状态、当前吞吐量和时延的详细信息是我们经常遇到的其他例子。
1.9进化式设计
变更控制并不一定意味着变更的减少 - 用正确的态度和工具,你可以频繁、快速且控制良好的改变软件。
当试图把软件系统组件化时,我们就面临着如何划分成服务的决策 - 我们决定分割我们的应用的原则是什么?组件的关键特性是独立的更换和升级的理念 - 这意味着我们要找到这样的点,我们可以想象重写组件而不影响其合作者。事实上很多微服务群组通过明确地预期许多服务将被废弃而不是长期演进来进一步找到这些点。
强调可替代性是模块设计更一般原则的一个特例,它是通过变更模式来驱动模块化的。 系统中很少变更的分应该和正在经历量扰动的分放在不同的服务里。如果你发现你自己不断地一起改变两个服务,这是它们应该被合并的一个标志。
把组件放在服务中,为更细粒度的发布计划增加了一个机会。对单体来说,任何变更都需要完整构建和署整个应用程序。而对微服务来说,你只需要重新署你修改的服务。这可以简化和加速发布过程。坏处是,你必须担心一个服务的变化会阻断其消费者。传统的集成方法试图使用版本管理解决这个问题,但是微服务世界的偏好是只把版本管理作为最后的手段。我们可以避免量的版本管理,通过把服务设计成对他们的提供者的变化尽可能的宽容。