第 1 章 微服务揭秘

第 1 章 微服务揭秘

微服务是一种架构风格,也是一种针对现代业务需求的软件开发方法。微服务并非发明出来的,确切地说是从之前的架构风格演进而来的。

本章将详细介绍从传统的单体架构到微服务架构的演进过程,还会介绍微服务的定义、概念和特性。

本章主要内容如下。

  • 微服务的演进。
  • 微服务架构的定义及相关示例。
  • 微服务架构的概念和特性。

1.1 微服务的演进

面向服务的架构(SOA)之后,微服务与DevOps以及云计算相辅相成,成为越来越流行的架构模式。微服务的演进很大程度上受到了当今商业环境中颠覆性数字创新的趋势和近几年技术演进的影响。下面详细介绍微服务的两个催化剂——业务需求和技术演进。

1.1.1 微服务演进的催化剂——业务需求

在当前的数字化转型时代,企业越来越多地将技术作为快速提升营收和客户基数的关键赋能手段。企业主要使用社交媒体、移动应用、云计算、大数据和物联网来实现颠覆性创新。企业运用这些技术寻找快速渗透市场的新方式,这给传统的信息技术交付机制带来了巨大的挑战。

图1-1比较了传统的单体应用和微服务应用在敏捷性、交付速度和扩展能力等当前企业面临的各项新挑战方面的表现。

图 1-1

 相比于传统的单体应用,微服务的敏捷性更高、交付速度更快、扩展能力更强。

企业花数年时间进行大规模应用开发的时代已经过去了。几年前,企业为了管理端到端的业务功能开发出了各种统一的应用,但是现在已经无人问津了。

图1-2展现了传统的单体应用和微服务应用在交付时间和成本上的差距。

图 1-2

 微服务支持快速开发敏捷应用,因而能降低总成本。

例如,当今航空公司不会投入资源将其核心主机订票系统重建为单体“巨兽”,金融机构不会重建其核心银行业务系统,零售商和其他行业也不会重建重量级的供应链管理系统,比如传统的ERP系统。各行业的焦点已经从构建大型应用转移到了以尽可能敏捷的方式构建能适应特定业务需求并快速取胜的各类单点解决方案。

以一个运行遗留单体应用的网络零售商为例。假设该零售商想基于顾客的购物偏好和其他信息向他们提供个性化的商品,进而革新既有销售方式;或者想基于顾客的购物喜好来推荐商品,从而引导顾客购买。

在这样的情况下,企业想快速开发一种个性化引擎或基于当前需求的推荐引擎,并将其插入遗留应用中,如图1-3所示。

图 1-3

如图1-3所示,投入大量资源来重建核心遗留系统是不明智的。要满足这种需求,要么如图1-3a所示,将遗留系统的响应依次传入两个新的功能模块做进一步处理;要么如图1-3b所示,修改核心遗留系统,让遗留代码调用这两个新的功能模块,从而完成整个处理。这两个新的功能模块通常以微服务的方式实现。

该实现方式给了软件开发团队许多试错机会。他们可以反复试验,以更低的成本快速尝试新的功能模块。之后,业务部门可以验证关键性能指标的变化或按需替换这些功能模块。

 现代系统架构应当以最小的成本,最大化随时替换系统组件的能力。微服务正是达到该目标的有效方式。

1.1.2 微服务演进的催化剂——技术演进

新兴技术促使人们重新思考构建软件系统的方式。比如几十年前,难以想象不通过两阶段提交协议来开发分布式应用。后来,NoSQL数据库彻底改变了这种思维方式。

与之类似,技术范式的转变已经重塑了软件架构的各个层面。

HTML5和CSS3的出现以及移动应用的发展,重新定义了UI。由于在响应式和自适应设计方面的突出表现,Angular、Ember、React、Backbone等客户端JavaScript框架流行开来。

随着云计算成为主流,Pivotal CF、AWS、Sales Force、IBM Bluemix、Redhat OpenShift等平台即服务(PaaS)厂商促使我们重新思考构建中间件组件的方式。Docker引发的容器化革命极大地影响了基础设施领域。Mesosphere DCOS等容器编排工具大大简化了基础设施管理。无服务器计算使得应用管理更加便捷。

随着Dell Boomi、Informatica、MuleSoft等集成平台即服务(iPaaS)的出现,系统集成领域的格局也发生了剧变。这些集成工具推动了软件开发团队将系统集成的边界扩展到传统企业应用之外。

NoSQL和NewSQL彻底革新了数据库领域。几年前,仅有的几种流行的数据库都是基于关系数据建模原理的。如今,数据库工具种类众多,例如Hadoop、Cassandra、CouchDB、Neo 4j和NuoDB等,都是针对特定的系统架构问题而设计的。

1.1.3 架构演进势在必行

应用架构一直都是随着苛刻的业务需求和技术演进而演进的。

不同的架构方法和风格,比如大型主机架构、客户机-服务器架构、N层架构和面向服务架构,都曾流行过。不管选用哪种架构风格,人们往往习惯于构建不同形式的单体架构系统。微服务架构是随着当今业务对敏捷性与交付速度等方面的需求、新兴技术的出现以及对前几代系统架构的学习而演进出来的。

如图1-4所示,微服务有助于打破单体应用的边界,构建由一系列逻辑上独立的小系统组成的系统。

图 1-4

 如果把单体应用看作由一个物理边界包围的一套逻辑子系统,那么微服务应用就是不存在物理边界的一套独立的子系统。

1.2 什么是微服务

微服务是一种架构风格。作为游戏规则改变者,它已被当今众多软件开发团队用于实现高度敏捷、快速交付和轻松扩展。微服务支持开发物理上隔离的模块化应用。

微服务并不是发明出来的。许多软件开发组织,比如Netflix、亚马逊和eBay,已经成功运用分而治之的技巧,在功能上将其单体应用分割为更小的原子单位,每个原子单位只实现单一的功能。这些公司解决了他们的单体应用所面临的一系列常见问题。随着这些企业的成功,其他许多企业也开始采用这种方式重构单体应用。之后,这种模式被命名为“微服务架构”。

微服务源自Alistair Cockburn在2005年提出的“六边形架构”思想。六边形架构也称“六边形模式”或“端口和适配器模式”。

简单说来,六边形架构提倡封装业务功能以与外界隔离。这些封装起来的业务功能无须感知周围的环境,甚至无须感知输入设备或输入渠道以及这些设备使用的消息格式。这些业务功能边缘的端口和适配器负责将从不同输入设备和渠道传来的消息转换为这些业务功能可以理解的格式。当要引入新设备时,开发人员就可以加入端口和适配器来支持这些设备,而无须修改核心的业务功能。开发人员可以用任意多的端口和适配器来实现需求。与此类似,外部实体也无须感知这些端口和适配器背后实现的业务功能,它们永远只需要和这些端口和适配器打交道。如此一来,开发人员就可以灵活地改变输入渠道和业务功能,而无须过多担心接口设计是否适用于未来的业务需求。

图1-5是六边形架构的概念图。

图 1-5

图1-5中,应用被彻底隔离并通过一套前端适配器和一套后端适配器与外界交互。前端适配器通常用于集成UI和其他API,后端适配器用于连接不同的数据源。前后端的端口和适配器负责将传入和传出的消息转换为外部实体能理解的格式。微服务架构的设计灵感正是源于六边形架构。

微服务没有标准的定义。Martin Fowler给出了如下定义。

“微服务架构风格是一种将单个应用开发为一套微小服务的方法。每个这样的微小服务在各自独立的进程内运行,采用轻量级的机制(通常是HTTP资源API)与外界通信。这些微小服务通常围绕业务能力进行构建,并且可以用完全自动化的部署工具独立部署。微服务架构最起码要实现集中式服务管理,服务本身可能是用不同的编程语言和不同的数据存储技术实现的。”

本书给出的微服务定义如下。

 微服务是一种架构风格或方法,用于构建由一套自包含、松耦合且自治的业务功能模块组成的信息技术系统。

图1-6展示的是一个传统的N层应用架构,包含了展现层业务逻辑层数据库层。模块A、模块B和模块C代表3个业务功能,图中的分层代表不同架构关注点间的隔离。每一层都包含了与该层有关的这3个业务功能。展现层包含了这3个模块的Web组件,业务逻辑层包含了这3个模块的业务逻辑组件,而数据库包含了这3个模块的所有数据表。在大多数情况下,不同层在物理上是隔离的,而同一层的不同模块是硬编码的。

图 1-6

基于微服务的架构如图1-7所示。

图 1-7

如图1-7所示,微服务架构中的边界被反转了。每个垂直的条状结构代表了一个微服务。每个微服务都有自己的展现层、业务层和数据库层。微服务是和业务功能相对应或一致的,这样一个微服务的变更不会影响其他微服务。

微服务间不存在标准的通信或传输机制。通常微服务之间的通信使用广泛采用的轻量级协议,比如HTTP和REST,或基于消息机制的协议,比如JMS或AMQP。在某些情况下,人们可能会选用优化的通信协议,比如Thrift、ZeroMQ、Protocol Buffers或Avro。

由于微服务与业务功能更为一致,并且其生命周期可独立管控,因此是企业开启DevOps和云计算之旅的理想选择。实际上,DevOps和云计算是微服务的两个重要方面。

 DevOps是一种重组IT资源的方式,可以缩小传统IT开发和高效运维之间的鸿沟。

1.3 微服务蜂巢

可以用蜂巢来比喻演进式微服务架构(见图1-8)。

图 1-8

在现实世界中,蜜蜂将这些六边形的蜡质巢室排列起来构造出蜂巢。蜜蜂使用不同的材料构造这些蜡质巢室。刚开始的时候蜂巢很小,整个蜂巢的构筑是由建造时可用的材料决定的。重复性的巢室形成了一种模式和强有力的织物结构。蜂巢中的每个巢室都是独立的,同时也和其他巢室集成在一起。通过增加新的巢室,蜂巢可以有序地“长”成一个巨大、结实的结构。封口后巢室内部的物质对外不可见。一个巢室的损坏并不会影响其他巢室,并且蜜蜂可以重建损坏的巢室而不会影响整个蜂巢。

1.4 微服务架构的设计原则

下面深入探讨微服务架构设计的一些原则。这些原则是设计和开发微服务系统时必须遵循的。其中最重要的两个原则是单一责任原则和自治原则。

1.4.1 每个服务承担单一责任

单一责任原则是SOLID设计模式中定义的一项重要原则。该原则要求每个单元应且只应承担一项责任。

单一责任原则要求一个单元,比如一个类、一个函数或者一项服务,只承担一项责任。在任何情况下,两个单元不能承担相同的责任,一个单元也不能承担多项责任。一个单元承担多项责任意味着紧耦合。

如图1-9所示,在一个电商应用中,客户、产品和订单是不同的功能模块。与将这些功能模块都构建到一个应用中相比,更好的做法是构建3个服务,每个服务仅负责实现一个业务功能。这样其中一个责任或功能模块的变化不会影响其他责任或功能模块。在该场景中,客户、产品和订单相当于3个相互独立的微服务。

图 1-9

1.4.2 微服务是自治的

微服务是自包含且可独立部署的自治服务,负责业务功能及其执行。微服务会捆绑所有依赖,包括第三方库依赖、执行环境依赖(比如Web服务器和容器)和抽象了物理资源的虚拟机依赖。

微服务和SOA的一个主要区别是服务的自治程度。虽然大多数SOA实现提供了服务级别的抽象,但微服务进一步抽象了实现和运行环境。

在传统的应用部署中,首先会构建一个war包或者ear包,然后将其部署到JEE应用服务上,比如JBoss、Weblogic、WebSphere等。当然,也可以将多个应用部署到同一个JEE容器中。而微服务的部署方式是将每个微服务构建为一个包含所有依赖的胖jar文件,然后在一个独立的Java进程中运行。

如图1-10所示,微服务也可以在自己的容器中运行。容器是跨平台且可独立管理的轻量级运行环境。容器技术(比如Docker)是部署微服务的理想选择。

图 1-10

1.5 微服务的特性

前面给出的微服务定义比较简单。布道师和实践者对微服务的看法不尽相同。目前还没有一个具体的且广泛接受的微服务定义,然而所有成功实现的微服务具有一系列共同特性。因此,比固守微服务的理论定义更为重要的是理解微服务的这些共同特性,下面详述。

1.5.1 服务是一等公民

在微服务领域,服务是一等公民。微服务抽象了所有实现细节,对外通过API暴露服务端点。微服务内部的实现逻辑、技术架构(比如编程语言、数据库、QoS机制等)完全隐藏在服务API之后。

而且,在微服务架构中不再开发应用了,软件开发团队将聚焦于开发服务。大多数企业需要在应用构建方式和企业文化上做出重大转变。

在客户信息微服务中,内部细节(比如数据结构、技术实现和业务逻辑等)都是隐藏的。这些细节对任何外部实体都是不可见或不暴露的。对该服务的访问会通过服务端点或API来进行限制。例如客户信息微服务可能会暴露客户注册和获取客户信息这两个API供外界访问和交互。

微服务架构中服务的特性

由于微服务或多或少像SOA,因此SOA中的许多服务特性对微服务同样适用。

下列服务特性对微服务同样适用。

  • 服务合约:与SOA类似,微服务也是通过明确定义的服务合约来描述的。在微服务领域,JSON和REST广泛用于服务之间的通信。就JSON/REST而言,有很多技术可用于定义服务合约,比如JSON Schema、WADL、Swagger和RAML等。
  • 松耦合:微服务是各自独立且松耦合的。在大多数情况下,微服务可以接收一个事件作为输入,然后触发另外一个事件作为响应。通常用消息机制、HTTP和REST实现微服务之间的交互。基于消息机制的服务端点可以实现更高层次的解耦。
  • 服务抽象:在微服务架构中,服务抽象不仅要抽象服务的具体实现,更要彻底抽象之前讲过的所有库和环境的细节。
  • 服务复用:微服务是粗粒度、可复用的服务,可以被移动设备、桌面应用、其他微服务甚至其他系统访问。
  • 无状态化:设计良好的微服务应当是无状态或不共享状态的。这些服务不需要维护共享的状态或会话状态。在确实需要维护状态时,这些状态通常在数据库或内存中进行维护。
  • 服务可发现性:微服务是可发现的。在典型的微服务环境中,服务会自我广播以便于外界发现。当服务终结后,它们会从微服务体系中自动消失。
  • 服务互操作性:由于微服务使用标准的协议和消息交换格式,因此它们之间是可互操作的。微服务通常使用消息和HTTP等传输机制。在微服务领域,REST/JSON是开发可互操作服务的最流行的方式。如果需要进一步优化通信方式,会用到其他协议,比如Protocol Buffers、Thrift、Avro或Zero MQ等。使用这些协议可能会限制服务之间的整体互操作性。
  • 服务可组合性:微服务是可组合的,可以通过服务编排或服务编制来实现。

1.5.2 微服务是轻量级的

设计良好的微服务对应单一的业务功能,只实现一个功能,所以大多数微服务实现的一个共同特性是服务运行所需的资源较少。

选择微服务的支撑技术(比如Web容器)时,必须确保这些技术本身也是轻量级的,以确保微服务整体的资源开销是可控的。例如相对于更为复杂的传统应用服务器,比如Weblogic或WebSphere,选择Jetty或Tomcat作为微服务的应用容器更好。

与hypervisor技术(比如VMware或Hyper-V)相比,容器技术(比如Docker)也有助于将基础设施的资源开销控制到最小。

如图1-11所示,微服务通常是部署到Docker容器中的,容器可以封装业务逻辑和运行时需要的库。这样做有助于快速将整套配置复制到新的机器上,或者完全不同的宿主环境中,甚至可以在不同的云供应商之间切换。由于不依赖物理的基础设施,容器化的微服务更容易实现跨平台。

{%}

图 1-11

1.5.3 微服务的混合架构

微服务是自治的,它将所有细节都抽象出来并隐藏到服务API之后,便于为不同的微服务设计不同的技术架构。微服务的实现过程包含如下共性。

  • 不同的服务可采用同一项技术的不同版本。一个微服务可能是在Java 1.7上开发的,而另一个微服务可能是在Java 1.8上开发的。
  • 不同的服务可采用不同的语言开发,比如一个微服务用Java开发,另一个微服务用Scala开发。
  • 可采用不同的存储架构,比如一个微服务用Redis缓存来存取数据,而另一个微服务可能使用MySQL作为持久化数据存储。

图1-12展示了混合语言的开发场景。

图 1-12

在本例中,由于预估酒店搜索服务的事务很多,对性能的要求也很高,因此使用Erlang语言来实现。为了支持预见性搜索,可以使用Elasticsearch作为数据存储。由于酒店预订需要更多ACID事务属性,因此用MySQL和Java来实现。内部的实现细节都隐藏在REST/JSON和HTTP定义的服务端点之后了。

1.5.4 微服务环境中的自动化

从开发环境到生产环境,大多数微服务实现是最大程度自动化的。

由于微服务将单体应用打散成了一系列小型服务,大企业中可能会出现微服务激增的现象。若非实现了自动化,大量的微服务将难以管理。微服务较小的资源开销也有利于将微服务从开发到部署的整个生命周期自动化。总体而言,微服务实现了端到端的自动化,比如自动化构建、自动化测试、自动化部署和弹性伸缩(见图1-13)。

图 1-13

如图1-13所示,自动化通常应用于开发、测试、发布和部署的各个阶段。

下面解释图1-13中的各个方块。

  • 开发阶段将使用版本控制工具(比如Git)和持续集成工具(比如Jenkins、Travis CI等)实现自动化。自动化工具也可能包括代码质量检查工具和自动化单元测试工具。微服务也能实现每次代码签入后自动触发全量构建。
  • 测试阶段将使用自动化测试工具(比如Selenium和Cucumber)和其他A/B测试策略来实现自动化。由于微服务对应业务功能,相较于单体应用,需要自动化的测试用例数量会少很多,因此方便了每次构建之后做回归测试。
  • 基础设施配备将通过容器技术(比如Docker)、发布管理工具(比如Chef或Puppet)以及配置管理工具(比如Ansible)来实现。自动化部署可以使用Spring Cloud、Kubernetes或Marathon等工具来实现。

1.5.5 微服务的生态支持系统

大多数大规模微服务实现有相应的生态支持系统,包括DevOps流程、集中化日志管理、服务注册、API网关、大量监控、服务路由和流量控制机制。

如图1-14所示,有了这些支撑能力,微服务才能正常工作。

图 1-14

1.5.6 微服务是动态分布式的

成功的微服务实现在服务中封装了逻辑和数据。这导致了以下两种不寻常的状况。

  • 分布式的数据和逻辑
  • 去中心化的服务治理

与传统应用将逻辑和数据聚合到一个应用边界中不同,微服务的数据和逻辑是去中心化的。每个服务对应一个特定的业务功能,因此其自身拥有数据和逻辑(见图1-15)。

图 1-15

图1-15中的虚线表示一个单体应用的逻辑边界。当把整个单体应用迁移到微服务架构后,每个微服务(例如A、B、C)会创建自己的物理边界。

通常微服务架构中集中式服务治理机制的运行方式和SOA架构中的不同。微服务实现的一个共同特征是不依赖重量级企业级产品,比如企业服务总线(ESB),而将业务逻辑和智能作为服务的一部分嵌在服务的实现中。

图1-16展示了一个使用ESB实现的零售系统的案例。

图 1-16

图1-16展示了一个典型SOA架构的实现方式。通过编排客户模块、订单模块和产品模块暴露出来的不同服务,完全用ESB实现购物逻辑。而在微服务的实现方式中,购物功能本身会以单独的微服务形式运行,该服务以一种解耦的方式与客户服务、产品服务和订单服务进行交互。

SOA架构的实现严重依赖静态服务注册和服务仓库的配置来管理服务和其他组件。微服务架构的服务注册和服务仓库配置则更为动态化。因此这种静态的服务治理方式在维护最新的服务信息时会产生额外的开销。这就是大多数微服务实现采用自动化的机制,利用系统的运行时拓扑来动态构建服务注册信息的原因。

1.5.7 抗脆弱、快速失败和自我愈合

抗脆弱是由Netflix试验成功的一项技术,是当今软件开发领域构建安全失败(fail-safe)系统最强有力的方式之一。

 抗脆弱的概念是由Nassim Nicholas Taleb在Antifragile: Things That Gain from Disorder一书中提出的。

在抗脆弱实践中,软件系统会不断面对各种挑战。软件系统通过应对这些挑战而演进,变得越来越经得起各种挑战,例如亚马逊的游戏日和Netflix的Simian Army。

快速失败(fast fail)是用于构建容错、弹性系统的另一个概念。该理念提倡系统应接纳失败,而不是构建从不出错的系统。关键是一旦出错,系统如何能从故障中迅速恢复。这种系统设计方式的重心从平均故障间隔时间(MTBF)转向了平均恢复时间(MTTR)。其关键优势是如果某个功能出错,它会结束自身而不影响下游功能。

自我愈合通常用于微服务的部署中,系统会自动从故障中学习并调整自身。这种自我愈合的系统也能避免之后出现错误。

1.6 微服务的实例

实现微服务并没有普遍适用的方法。下面分析不同的微服务实例以厘清微服务的概念。

1.6.1 一个酒店门户网站的例子

首先以一个假日门户网站Fly By Points为例。Fly By Points用户通过该网站预订酒店、航班或租车会获得相应积分。用户登录该网站后可以看到自己的积分、积分可以兑换的专属优惠以及近期行程等,如图1-17所示。

图 1-17

假设图1-17所示的页面是登录后的首页。Jeo近期有两趟行程,有4项专属优惠和21 123积分。当用户点击其中任意一个方块,系统就会查询并显示详细信息。

该假日门户网站使用的是基于Java和Spring的传统单体应用架构,如图1-18所示。

图 1-18

如图1-18所示,该假日门户网站的架构是基于Web和模块的,不同层级之间分工明确、彼此隔离。该网站遵循惯例,把单独的war文件部署到了Web服务器(比如Tomcat)之上。数据存储在一个复杂的后端关系型数据库中。这对于不太复杂的系统架构来说是可行的,但是当业务发展、用户基数膨胀时,系统会变得复杂。

这也导致了系统事务成比例地增长。对此,企业应当设法将单体应用重构为微服务架构,以获得更高的交付速度、敏捷度和可控度,如图1-19所示。

图 1-19

这是一个应用的简化微服务版本,该架构有如下特点。

  • 每个子系统本身即一个独立的系统——微服务。这里有3个微服务,代表了3项业务功能——行程优惠积分。每个微服务有自己的内部数据存储和中间件,并且内部结构保持不变。
  • 每个微服务封装了各自的数据库和HTTP监听器。跟之前的单体架构模型不同,这里没有Web服务器和war包,每个服务都拥有嵌入式的HTTP监听器,比如Jetty和Tomcat等。
  • 每个微服务暴露了一套REST服务来操作属于该服务的资源/实体。

假设展现层是用客户端JavaScript MVC框架(比如Angular)来开发的。这些客户端框架能直接发起REST调用。

加载页面,行程优惠积分这3个方块都会显示积分、优惠数目和行程数目等详细信息。这可以通过每个方块单独以REST方式向对应后端微服务发起异步调用来实现。在服务层,服务之间不存在相互依赖。当用户点击任意一个方块时,界面会切换并加载该项目的细节。这是通过向对应的微服务发起另一个调用来实现的。

1.6.2 一个旅行社门户网站的例子

另一个例子是一个简单的旅行社门户应用,涉及同步REST调用和异步事件。

在这种情况下,门户网站仅仅是一个容器应用,里面有许多菜单项和链接。当请求特定页面时,比如当点击菜单项或链接时,会从特定微服务加载相应页面。

由多个后端微服务支撑起来的旅行社门户网站的架构如图1-20所示。

图 1-20

当客户请求预订时,系统内部会触发以下事件。

  • 旅行社打开航班UI页面,为客户搜索合适的航班。在后台,航班UI页面会从航班微服务加载信息。航班UI页面只与航班微服务内部的后端API交互。在这种情况下,航班UI页面会向航班微服务发起REST调用来加载并显示航班信息。
  • 然后旅行社访问客户UI页面来查询客户信息。与航班UI页面相似,客户UI页面会从客户微服务加载。客户UI页面中的控制器会向客户微服务发起REST调用,客户信息便会通过调用客户微服务上对应的API加载出来。
  • 之后,旅行社检查客户签证信息,以判断客户能否到目标国家旅行。这里也采用了之前两点提到的模式。
  • 然后旅行社通过预订UI页面为客户预订服务,预订UI是从预订微服务加载而来的,这里也采用了相同的模式。
  • 支付UI页面会从支付微服务加载。通常支付服务会有一些额外的约束,包括支付卡行业数据安全标准(PCIDSS)合规性,比如加密和保护传输中和存储后的数据。微服务架构方法的优势是PCIDSS不管控支付以外的微服务。而在单体应用架构中,PCISDSS管控整个应用。支付服务也采用了相同的模式。
  • 预订提交后,预订微服务会调用航班服务来验证和更新航班预订信息。这种服务编排是作为预订服务的一部分而定义的。预订决策的逻辑也是在预订服务内部实现的。作为预订流程的一部分,预订微服务也会验证、获取和更新客户信息。
  • 最后,预订微服务会发出一个预订事件,通知微服务接收该事件并通知客户。

有趣的是,这里可以更改一个微服务的UI、逻辑和数据,而不会影响其他微服务。

这种架构方法简洁、优雅。许多门户应用,尤其是面向不同用户群体的门户网站,可以用不同的微服务组装成不同的界面而构建出来。整体的行为和导航方式是由门户应用控制的。

除非这些门户页面在设计之初就考虑到了这种架构方法,否则这种架构方法也会面临不少挑战。请注意,站点布局和静态内容是作为布局模板从内容管理系统(CMS)加载的,也可能存储在Web服务器中。站点布局可能包含UI的片段,这些片段会在运行时从微服务加载。

1.7 微服务架构的优势

相对于传统的多层单体架构,微服务架构有许多优势,下面介绍一些关键优势。

1.7.1 支持混合架构

在微服务架构中,开发者和架构师可以自由选择最适合既定场景的技术和架构。这让他们可以灵活地设计合适且经济的解决方案。

由于微服务是自治且独立的,因此每个服务都可以在自己的架构或技术,甚至是同一技术的不同版本之上运行。

图1-21展示了一个简单实用的微服务混合架构的例子。

图 1-21

其中有个需求是审计所有系统事务和记录事务的细节,比如请求数据和响应数据、触发事务的用户和调用的服务等。

如图1-21所示,核心服务(例如订单服务和产品服务)使用了关系型数据库,而审计服务将数据持久化到了Hadoop文件系统(HDFS)中。关系型数据库对于存储大数据量(比如审计数据)来说既不理想,性价比也不高。在单体架构方法中,应用通常使用单一的共享数据库来存储订单、产品和审计数据。

在本例中,审计服务是一个技术型微服务,使用了不同的技术架构。类似地,不同的功能服务也可以使用不同的技术架构来实现。

在其他例子中,可能会有一个预订微服务在Java 7上运行,而搜索微服务在Java 8上运行。同样,一个订单服务可能是用Erlang编写的,而配送服务可能是用Go语言编写的。这些在单体架构中是无法实现的。

1.7.2 为试验和创新赋能

现代企业在快速取胜中谋发展。微服务是企业进行颠覆性创新的关键赋能者之一,企业可以利用微服务进行试错和快速失败。

由于微服务小而简单,因此企业可以尝试新的流程、算法和业务逻辑等。对于大规模单体应用而言,试错却不那么简单、直接或性价比高。企业必须花费大量资金才能通过构建或更改应用来尝试新事物。但是利用微服务架构,企业可以通过编写小型微服务来实现目标功能,并响应式地将该其插入系统中。然后企业就可以花时间试验新功能了。此外,如果这个新的微服务并没有像预期那样工作,可以更改或者用其他微服务替换它。与单体架构方法相比,更改微服务的成本更低。

在另一个航班预订网站例子中,航空公司想在预订页面显示个性化的酒店推荐信息。这些推荐信息必须在预订成功的页面上显示。

如图1-22所示,编写一个可以插入该单体应用预订流程中的微服务很简单,而要将该需求并入这个单体应用中却不容易。这家航空公司可能会选择从简单的推荐服务开始,不断用新版本替换它,直到该服务达到了公司期望的推荐准确度。

图 1-22

1.7.3 弹性伸缩和选择性扩容

由于微服务实现了单元更小的功能,因此可以实现选择性扩容和其他一些服务质量(QoS)管理。

即使在一个应用中,扩展需求可能也会随着功能模块的不同而不同。单体应用通常打包为单个的war包或ear包。因此,应用只能作为一个整体来进行扩展,而无法单独对一个模块或者在一个子系统级别上进行扩展。当大量数据高速涌入时,一个I/O密集型功能模块可以轻松降低整个应用的服务级别。

在微服务架构中,每个服务可以独立地扩展。由于扩展性可以有选择性地应用于每个服务,因此微服务架构方法的扩展成本相对较低。

在实践中,一个应用有多种扩展方式,具体选择很大程度上会受到系统架构和应用行为的约束。扩展立方体定义了扩展应用的3种主要方式。

  • X 轴扩展,通过水平克隆应用来实现。
  • Y 轴扩展,通过拆分不同的业务功能来实现。
  • Z 轴扩展,通过对数据进行分区或分片来实现。

Y轴扩展应用于单体应用时,它会把该单体应用打散成许多小的功能单元,而这些功能单元和业务功能是一一对应的。许多软件开发团队选用了该技术而远离了单体应用。这些拆分出来的功能单元和微服务的诸多特性大体一致。

比如在一个典型的航空公司网站上,统计数据显示航班搜索量和航班预订量的比例可能高达500∶1。这意味着每个预订事务对应了500个搜索事务。在该场景中,搜索服务需要的扩展能力是预订服务的500倍。图1-23给出了一个很好的选择性扩容示例。

{%}

图 1-23

解决方案就是区别对待搜索请求和预订请求。对于单体架构而言,唯一可行的是用扩展立方体中的Z轴扩展方式来实现。然而,这种扩展方式成本很高,因为需要复制整个代码库。

在图1-23中,搜索功能和预订功能设计成了不同的微服务,使得它们彼此独立,可以进行不同的扩展。图中搜索服务有三个实例,而预订服务只有两个实例。选择性扩容其实并不局限于扩展实例的数量(如图1-23所示),也能以不同的方式设计微服务架构。就搜索服务而言,可以用一种内存数据网格(IMDG)技术(比如Hazelcast)作为数据库,这样能更好地提升搜索服务的性能和扩展能力。当一个新的搜索服务实例化后,IMDG集群中就会加入一个新的IMDG节点。而预订服务不需要同等的扩展能力。预订服务的两个实例连接到数据库的同一个实例。

1.7.4 服务可替换

微服务是自包含且可独立部署的模块,这使得可以将一个微服务替换为另一个相似的微服务。

许多大型企业在实现软件系统时会选择购买或构建(自开发)。通常情况是企业内部构建大部分业务功能,而向外部专业组织购买特定的补充功能。这会给传统的单体应用带来极大的挑战,因为这些单体应用内部的组件之间是高度耦合的。尝试将第三方解决方案插入单体应用中需要非常复杂的集成工作。对于微服务架构来说,这种做法并不是事后的补救办法。就架构而言,一个微服务易于被另一个自开发的或者从第三方微服务扩展而来的微服务替换,如图1-24所示。

图 1-24

航空业务中的价格引擎模块非常复杂。不同行程的机票价格是通过定价逻辑,一套非常复杂的数学公式计算出来的。航空公司可能会选择从市场上购买现成的价格引擎,而不是自己开发一个产品。在单体架构中,定价功能是票价预订的函数。在大多数情况下,定价、票价和预订功能是硬编码的,所以几乎无法分离。

在一个设计良好的微服务系统中,预订、票价和定价功能是独立的微服务。替换定价微服务对其他服务的影响极其微小,因为这些微服务之间都是独立和松耦合的。可能今天用了一个第三方服务,而明天用另一个第三方服务或者自己开发的服务轻松地将其替换掉。

1.7.5 为构建有机系统赋能

微服务有助于构建本质上有机的系统。在将单体系统逐渐迁移到微服务系统的过程中,这一点极为重要。

有机系统指在一段时间内通过加入越来越多的功能实现横向扩展的系统。实际上,应用在其整个生命周期中会逐渐变得庞大,而且在大多数情况下,其可维护性会急剧下降。

微服务是可独立管理或维护的服务,这使得我们可以按需加入更多服务,而把对现有服务的影响降到最小。构建这样的系统不需要巨大的资金投入,因此企业可以用部分运营费用来不断构建新服务。

有个航空公司的客户忠诚度系统是多年前构建的,该系统是面向个体乘客的。在航空公司开始向企业客户提供忠诚度福利之前,该系统一直运行良好。企业客户指的是归到企业名下的个人客户。由于当前系统的核心数据模型是扁平化、面向个人客户的,实现企业客户的需求就需要对核心数据模型做出重大改变,因而需要大量返工来实现该需求。

如图1-25所示,在基于微服务的架构中,客户信息由客户微服务管理,忠诚度信息由忠诚度微服务管理。

图 1-25

在这种情况下,加入一个新的企业客户微服务来管理企业客户信息是很容易的。当一家企业注册时,个人会员跟之前一样由客户微服务来管理。企业客户微服务会对客户微服务提供的数据进行聚合而提供企业概况。该微服务也能提供服务来支持企业的特定业务规则。用这种方法添加新服务可最大程度降低对现有服务的影响。

1.7.6 有助于管理技术债

由于微服务都比较小,服务之间的依赖也很小,因而可以用最低的成本来迁移目前还在采用过时技术的服务。

技术变迁是软件开发中的一个障碍。在许多传统单体应用中,由于技术更新迅速,今天的所谓“下一代应用”甚至在投入生产之前就沦为遗留系统了。架构师和开发者往往通过加入各种抽象层来克制技术变迁。然而这种方式并不能解决问题,甚至还会导致系统过度设计。由于技术的更新换代通常是有风险且成本高昂的,而对于业务来说又没有直接的产出或回报,因此企业可能不会投入资金来减少这些应用的技术债。

在微服务中,每个服务都可以单独更改或更新,而无须更新整个应用。

比如将一个用EJB 1.1和Hibernate编写的有500万行代码的应用升级到Spring、JPA和REST服务几乎相当于重写整个应用。但是在微服务领域,这种升级可以用递增的方式实现。

如图1-26所示,虽然旧版本的服务在旧版本的技术上运行,但新服务的开发可以采用最新技术。与优化现有单体应用相比,将尚在使用过时技术的微服务迁移到新技术的成本会大大降低。

图 1-26

1.7.7 允许不同版本并存

由于微服务把服务运行时环境和服务本身一并打包了,使得多个版本的服务可以在同一个环境中并存。

同一个服务的多个版本同时运行并不鲜见,例如零宕机升级,它要求服务必须优雅地从一个版本切换到另一个版本,并且会存在两个版本的服务必须同时启动运行的时间窗口。对单体应用而言,这个过程很复杂,因为在集群中某个节点上升级新服务会比较麻烦,会导致类加载等问题。金丝雀发布是将一个新版本仅发布给少数用户去验证新服务,这是服务的多个版本必须并存的又一个例子。

这两种场景用微服务架构都易于实现。由于每个微服务都有独立的运行环境,包括服务监听器,例如嵌入式Tomcat或Jetty,所以微服务的多个版本可以同时发布并且可以优雅地过渡到新版本,而不会出现很多问题。消费者在查找服务时,会查找该服务的特定版本。例如在一次金丝雀发布中,有一个新的用户界面发布给了用户A。当用户A发送请求至微服务时,他会查找该微服务的金丝雀发布版本,但是其他用户仍继续查找最新生产版本。

但是在数据库层面必须格外小心,为了避免出现破坏性的变更,应确保数据库设计始终是向后兼容的。

如图1-27所示,客户服务的V01和V02两个版本并存,因为它们有各自的部署环境,所以这两个版本不会互相干扰。

图 1-27

如图1-27所示,可以在网关上设置路由规则将流量分流至特定服务实例。另外,作为请求本身的一部分,客户端也可以请求特定版本的服务。在图1-27中,网关会基于发起请求的不同地域来选择服务的不同版本。

1.7.8 支持构建自组织系统

微服务支持构建自组织系统。支持自动化部署的自组织系统是弹性系统,并且能够自我愈合和自我学习。

在一个架构设计良好的微服务系统中,服务之间是不会互相感知的。服务会从选定的队列中接收消息并处理。处理结束后,该服务可能会发出另一条消息来调用其他服务。这使得我们可以将任意服务加入这个生态系统中,而无须分析这对整个系统的影响。基于输入和输出,服务会将自身融合到生态系统中,而无须额外的代码变更或服务编排。整个过程不需要中央大脑来控制和协调。

假设有一个现成的通知服务会监听INPUT队列,然后发送通知消息到简单邮件传输协议(SMTP)服务器,如图1-28所示。

{%}

图 1-28

如果日后需要引入个性化引擎,在把消息发送给客户之前将消息个性化,比如将消息语言翻译为客户的母语。

更新后服务之间的连接关系如图1-29所示。

{%}

图 1-29

可以使用微服务架构创建新的个性化服务来实现该功能。其中的输入队列会在一个外部配置服务器中配置为INPUT。该个性化服务会从INPUT队列(之前是通知服务使用的)取消息,然后在完成相应处理后向OUTPUT队列发送消息。随后通知服务的输入队列会将消息发送到OUTPUT队列,之后系统会自动采用这种新的消息流机制。

1.7.9 支持事件驱动架构

微服务架构支持开发透明的软件系统。传统系统之间通过本地协议通信,因而系统行为类似于黑盒应用。除非业务事件和系统事件是显式发布出来的,否则难以理解和分析。如今的应用都需要使用数据进行业务分析、理解系统的动态行为,或者分析市场趋势,而且这些应用也需要对实时事件做出响应。事件机制对于数据提取是很有用的。

一个架构设计良好的微服务系统始终需要处理输入和输出事件。任何服务都可以使用这些事件。事件提取出来后就可以用于许多场景了。

例如企业想实时查看根据产品类型分类的订单量。在一个单体架构系统中,需要考虑如何提取这些事件,而这很可能会导致系统变更。

图1-30展示了在不影响现有服务的前提下加入新的事件聚合服务。

图 1-30

在微服务领域,每次创建订单都会发布订单事件。这仅相当于增加新服务而已。新服务可以订阅相同的主题,提取事件,进行相应的聚合,最后发布另一个事件让仪表盘组件来处理。

1.7.10 为DevOps赋能

微服务架构是DevOps的关键赋能者之一。在许多企业中,DevOps已被广泛采用,旨在提高项目的交付速度和敏捷度。采用DevOps,需要在文化上、流程上和架构上做出改变。DevOps提倡敏捷开发、快速发布、自动化测试、自动化基础设施配备和自动化部署。对于传统单体应用而言,将这些过程自动化很难实现。虽然微服务不是最终的答案,但在许多DevOps项目实现中微服务占据了举足轻重的位置。许多DevOps工具和技术也正是通过微服务架构来演进的。

考虑到一个单体应用通常需要数小时完成一次全量构建,然后需要20到30分钟启动应用,显然,这种应用对于实现DevOps自动化来说并不理想,每次提交之后的自动化持续集成也比较困难。由于大型单体应用对自动化并不友好,持续测试和发布也就很难实现。

而微服务的资源开销较小,对自动化更友好,因此能更好地支持DevOps的这些需求。

微服务也可为规模小而专注于特定方向的敏捷开发团队赋能。这些团队可以基于不同的微服务边界组织起来。

1.8 小结

本章通过一些实例介绍了微服务的基本原理。

本章介绍了从传统单体应用到微服务架构的演进过程、现代应用架构的一些原则和思维方式上的转变,还总结了大多数成功的微服务项目实现的共同特征,最后讨论了微服务架构的优势。

下一章会分析微服务架构和其他架构风格之间的联系,还会介绍微服务的常见用例。

目录

  • 版权声明
  • 前言
  • 第 1 章 微服务揭秘
  • 第 2 章 相关架构风格和用例
  • 第 3 章 用Spring Boot构建微服务
  • 第 4 章 应用微服务概念
  • 第 5 章 微服务能力模型
  • 第 6 章 微服务演进案例研究
  • 第 7 章 用Spring Cloud组件扩展微服务
  • 第 8 章 微服务的日志管理和监控
  • 第 9 章 用Docker容器化微服务
  • 第 10 章 用Mesos和Marathon扩展容器化的微服务
  • 第 11 章 微服务开发生命周期