前文:
【微服务架构】微服务与SOA架构(1)
【微服务架构】微服务与SOA架构(2)
比较架构特性
组件(component)是软件中的一个单位,具有定义良好的接口、定义良好的角色/责任集合。组件是架构的构成元素。对于基于服务的架构,这些构成元素通常被称为服务(或者服务组件)。不管组件带上什么标签,当创建一个架构时,你都需要决定组件如何被共享、组件间如何通信、多个组件如何被整合起来完成业务请求以及如何从远程服务用户的位置访问他们。
为这些问题做出决定并不是件容易的事情,这就是为什么需要了解架构模式的原因。每种架构模式都有独特的拓扑结构用来定义架构的形状和一般属性,包括组件之间如何相互关联、如何通信、如何协作完成业务请求等等。通过分析架构模式的拓扑结构,你可以更好地决定哪种模式更适合你要开发的应用。
本章我主要分析微服务和SOA之间在总体结构拓扑、架构模式的决定性属性等方面的差异。值得一提的是,这两种架构模式之间的差异主要表现在服务组件的共享程度、服务组件的通信水平以及远程服务组件的典型访问方式。本章也会讨论在SOA架构模式中常见的消息中间件与微服务架构模式中常见的可选API层之间的不同。
组件共享
在组件共享方面,微服务与SOA是有本质上的差异的。SOA是建立在“能共享就共享(share-as-much-as-possible)”的架构风格之上的,而微服务是建立在“能不共享就不共享(share-as-little-as-possible)”的架构风格之上。本节中我会分析这两种概念与其对应的微服务和SOA之间的不同。
组件共享是SOA中的核心概念之一。事实上,组件共享也是企业服务的重中之重。例如,考虑一个大型零售公司,如图3-1所示,有很多应用跟订单处理有关,例如客户管理系统、库存管理系统和订单执行系统。所有这些系统都有自己的Order(订单)服务版本。本例中,假设更新订单的流程需要特殊业务逻辑,这意味着该特殊业务逻辑需要在企业的多个应用之间复制,并且需要应用之间额外的验证和协调。图3-1中还需要注意的是,每个系统都有各自的数据库,因此每个系统中都可能对一个订单有自己的表现形式。
SOA期望通过企业级的共享服务(企业服务)来解决这个问题。回到前述的零售案例,假设现在新生成了一个集中共享的Order企业服务,如图3-2所示,更新订单所关联的同一业务逻辑可以被每个应用共享。
注意在图3-2中,尽管Order服务是共享的,它仍然在访问三个不同数据库。这在SOA架构采用“能共享就共享”风格时是一个关键概念。Order服务足够智能,知道对不同系统而言需要从哪些数据库检索数据并更新订单数据,同时在三个系统之间同步数据。也就是说,订单并不是通过一个而是三个数据库的组合来完成表述。
尽管“能共享就共享”的架构解决了与业务功能重复相关的问题,但是却带来了组件间耦合过于紧密的问题,增加了与变更相关的整体风险。例如,假设对图3-2中的Order服务做了某个变更,因为Order服务是一个企业服务,对全公司范围可见,很难测试该服务的全部使用场景以确保这次变更不会影响到企业其他部门。
微服务架构,是基于“能不共享就不共享”的理念的,利用了一个领域驱动设计中的概念限定语境(bounded context)。架构上来看,限定语境是指组件(或者这里的服务)与其所关联的数据紧密耦合成一个封闭的单元,与外界有着最小的依赖。这种方式设计的服务组件本质上都是自包含的,只开放定义良好的接口和定义良好的合约。
切合实际地说,即使在微服务架构中也总有某些服务(例如,基础服务)是共享的。然而,SOA架构总是倾向于更大化组件共享,而微服务架构则通过限定语境的概念最小化共享。一种实现限定语境并将依赖最小化的极端方式是违反“不要重复自身(Don’t Repeat Yourself),DRY”原则,将公共性的功能在多个服务中复制,以获得全局上相互独立的效果。另外一个方式是将相对静态的模块编译到共享库中,以便服务组件可以在编译时或者运行时将它们链入。我的好朋友和同事Neal Ford对此有些不同的看法,他认为微服务架构是一种什么都不共享的架构,只有两个例外:一是如何实现服务之间的整合,而是如何夯实基础服务以确保(其它服务)在工程实现上的一致性。
发挥限定语境的概念有很多优点。首先,因为不存在依赖模块,服务的维护变得容易很多,服务的变更和演进都可以相互独立地进行;其次,部署也变得容易很多,需要部署的代码变少,对一个模块的变更也不太可能影响到应用的其它部分。用这种方式生成的应用更加稳定,很少会受到服务变更的副作用影响。
服务编制与编排
服务调配(orchestration)与编排(choreography)之间的区别并总不是很明显。本节中会解析两者之间的区别,以及这两种服务通信概念是如何在SOA和微服务中使用的。
术语服务调配指的是多个服务通过一个集中式调节者进行协调。这里的调节者可以是服务的客户,也可以是一个集成枢纽(Mule、Camel、Spring Integration等)。图3-3展示了服务调配的概念。
理解服务调配的最容易的方法就是想象一个管弦乐队的构成。若干音乐家们在不同乐章中使用不同的乐器进行演奏,但他们的演奏都由乐队指挥一个人协调。同样地,服务调配中的协调者组件就扮演着乐队指挥的角色,负责协调所有服务调用以完成业务事务。
服务编排指的则是没有中央协调者的情况下多个服务调用之间的协调。讨论服务编排时,有时会涉及到术语“服务间通信(inter-service communication)”。通过服务编排,某个服务调用另外一个服务,而后者又调用其他服务等等,形成一个服务链(service chaining)。图3-4所展示的就是这个概念。
理解服务编排的一种方法就是想象一个在台上表演的舞蹈团。所有舞者都相互同步地移动身位,但是没有人指挥或者指导每个舞者。舞蹈是通过每一个舞者与其他人共同协作实现的,而音乐会则是通过某个指挥的协调来实现的。
微服务架构更适合采用服务编排而不是服务调配,主要是因为这种架构的拓扑结构中没有一个集中式的中间件组件。图3-5所展示的全局架构拓扑中包含两类主要组件:服务组件和(是可选的)非智能化的API层。(我会在下一节讨论API层及其扮演的角色)。从实现的角度来看,架构中还可以有其他组件,例如服务注册和发现组件、服务监控组件和服务部署管理器等等,但是就微服务结构模式的服务分类法而言,它们更应该被视作基础服务。
因为微服务架构是一种“能不共享就不共享”的架构,你应该尽量减少服务编排的数量,尽可能让功能服务与基础服务进行交互。如前一章所述,如果发现在功能服务之间需要进行大量的服务编排工作,很有可能是因为你的服务粒度太细了。
在微服务架构中执行太多的服务编排可能会导致出耦合度(efferent coupling)太高的问题。出耦合度这个术语指的是某个组件为了完成某个业务请求在多大程度上需要依赖其它组件。考虑图3-6中的例子,展示了处理订单请求时所需要的三个服务:validate order(验证订单),place order(下单)和notify customer(通知客户)。架构上来说,这个业务请求有很高的出耦合度,而这正是架构师们在采用大多数微服务架构中尽量避免的。
服务编排中中发生的服务耦合问题可能引起性能不佳以及应用不稳定的问题。正如前一章所讨论的,因为微服务架构下的服务一般都是远程服务,使用服务编排进行服务协调时所产生的每个服务调用都会延长请求的响应时间,因为远程访问协议的通信传输时间总是存在的。另外,为同一业务请求而对多个服务进行协调时,调用链上的任何一个服务都可能出现不可用或者不响应的状况,从而会导致应用可靠性和稳定性都会下降。
为解决微服务架构中对多个功能服务进行编排的问题,可以考虑将多个细粒度服务合并成一个粗粒度服务。如果某个细粒度服务刚好被另外几个服务共享,那么可以保持它独立。或者,也可以根据此功能的规模及自身属性,有意违反DRY原则,将这一共享功能复制到每个粗粒度服务中。
图3-7中展示的是如何通过将三个细粒度服务整合为一个粗粒度服务,消除服务编排从而解决与服务编排相关的问题。这里所指的问题具体包括:首先,因为减少了远程调用,整体的性能会得到提升;第二,因为服务可用性的问题出现得更少,整体的稳定性得以改进;最后,因为不再需要远程服务合约,总体上的部署和维护得以简化。
SOA,作为一种“能共享就共享”的架构,同时依靠服务调配和编排来完成业务请求的处理。如图3-8所示,SOA中的消息中间件,通过调用多个企业服务来处理同一业务服务请求,从而完成服务调配。一旦加入到企业服务中,服务编制组件可以用于调用应用服务或者基础服务来帮助完成特定业务请求。
图3-8展示了SOA架构中使用服务编排的几种变化形式。例如,企业服务可能需要调用应用服务,而此应用服务又需要调用一个基础服务完成业务处理。另一种情况是,企业服务也可能只需要调用一个应用服务或者一个基础服务。还有可能业务逻辑直接自包含在企业服务中而不需要任何服务编排。
微服务和SOA在服务调配和编排方面的不同,进一步说明二种模式在架构特征方面存在诸多差异,包括性能、开发、测试和部署等等。因为SOA一般依赖多个服务(和服务类型)来完成同一业务请求,基于SOA架构构造的系统一般比微服务慢,而且需要更多时间和精力进行开发、测试、部署和维护。实际上,这也是让架构师慢慢从SOA转向更为简单和直接的微服务架构的部分原因。
中间件与API层
如果比较前一节中的图3-5和3-8,你就会注意到两种架构模式中都存在一个中间件组件来执行调度。然而,事情并不是这样的。微服务架构模式中通常包含一个称作“API层”的组件,而SOA中所包含的是消息中间件组件。本节中我们会比较这两种组件所扮演的角色和所提供的能力。
微服务模式不支持消息中间件(例如,集成枢纽或者企业服务总线)的概念,而是支持在所有服务的前端设置一个API层作为服务接入层。在客户和服务之间放置一个API层通常是个不错的主意,因为这个组件实质上构造了一个抽象层,使得客户不需要知道服务端的确切位置。同时也使得服务粒度的改变不会影响到服务客户。对服务粒度进行抽象的确需要在API层提供一定的智能和一定程度的调配能力,但这些问题可以慢慢通过重构解决,重要的是服务端可以根据需要演化,而不是要求服务的客户也经常做出变更。
例如,假设有个服务跟产品订单业务功能有关。我们发现它的粒度太粗了,想把它分解成两个粒度更细的服务,从而提高服务的伸缩能力并简化部署。如果没有API层来为实际的服务端提供抽象,使用该服务的每个客户都要做出变更,从调用一个服务转为调用两个服务。如果使用了API层,服务的客户端就不需要知道(甚至不在乎)同一请求现在将被分解成两个服务调用。
SOA依赖于其消息中间件来协调服务调用。使用消息中间件(我更愿意称为集成枢纽)可以获得一些在微服务架构风格中不存在的额外的架构特性,包括调解和路由、消息增强、消息转换和协议转换等。
调解和路由(mediation and routing)指的是架构基于特定业务或者用户请求来对服务进行定位和调用的能力。这种能力如图3-9所示。注意本图中对服务注册或服务发现组件的使用,以及对服务调配能力的利用。微服务和SOA都有这种能力,特别是服务注册或者服务发现模块所提供的能力。不过,服务调配在微服务中被严格控制甚至排除,而在SOA中却是经常用到的能力。
消息增强(message enhancement)指的是架构在请求的数据部分到达服务之前之前对其进行修改、删除或者增加的能力。消息增强的例子包括改变日期格式、添加额外数值或者查询数据库进行数据转换,例如将统一安全鉴定程序委员会(Committee on Uniform Security Identification Procedures,CUSIP)代码转换为股票代码或者从后者转换为前者。微服务模式不提供这种能力,主要是因为其架构中不包含实现这一功能的中间件组件。SOA通过其消息中间件完全支持这种能力。
图3-10展示了这种能力。注意,图中客户所发送的是CUSIP代码(一种标准交易标识码)和格式为MM/DD/YY的日期数据,而服务期望接收的是证券交易所每日正式牌价表(Stock Exchange Daily Official List,SEDOL)代码(另外一种交易标识码)以及格式为YYYY.MM.DD的日期以及股票代码(假设是股票交易)。本例中,消息中间件将CUSIP代码(苹果公司是037833100)转化为SEDOL数(苹果公司是2046251),查询并添加代码(AAPL),将日期从04/23/15转化为2015.04.23。
消息转换(message transformation)指的是架构将一种数据格式转换为另外一种格式的能力。例如,如图3-11所示,客户调用服务并以JSON格式发送数据,而服务期望接收的是Java对象。注意消息增强『译注:似应为消息转换』并不关心请求相关的数据本身,而只是关注数据格式的转换。微服务架构并不提供这种能力,而SOA架构则通过消息中间件提供这种功能。
最后,协议转换(protocol transformation)所描述的是架构允许客户采用与服务端预期不匹配的协议来调用服务的能力。图3-12展示了这种能力。注意,图中服务客户采用REST进行通信,但是负责处理请求的服务要求建立RMI/IIOP链接(例如,Enterprise JavaBeans 3 [EJB3])和AMQP连接。微服务可以支持多种协议类型,但是服务的客户和服务必须采用同一通信协议。在SOA架构中,多个协议则可以根据需要混合使用。
我会在下一章中详细讨论这些能力,因为它们跟微服务和SOA的架构能力比较更相关。
访问远程服务
由于微服务或者SOA架构中服务通常是远程访问的,这些架构模式都需要提供一种供客户访问远程服务的方式。就远程访问而言,两种架构模式的根本差别在于,微服务倾向于把REST作为首选远程访问协议,而SOA则没有这种限制。事实上,可以同时处理多种不同远程访问协议是SOA与微服务架构相较最关键的不同点之一。
微服务有一个基本原则是技术和架构选项非常受限,这也使得该架构模式比较简单。例如,大多数微服务架构都仅使用两种访问协议:REST和简单消息(JMS、MSMQ、AMQP等)协议。这并不是说,不能采用其他远程协议,例如SOAP或者.NET Remoting,而仅仅是说一般微服务都倾向于选用同质协议。换句话说,服务要么是基于REST的,要么基于消息的,要么是基于其他协议的,但是在同一应用或者系统内是很少会混合使用多种协议。一个例外也就是可能出现混合使用协议的状况是,依赖发布订阅(publish-and-subscribe)广播模式的服务可能是基于消息的,而其他非广播式服务都是基于REST的。
SOA则没有对架构模式中采用哪种远程访问协议进行限制。在下一章中可以看到,架构中的中间件组件能够支持任意数量的远程访问协议,并提供各种协议之间的转换。尽管如此,大部分SOA架构通常依赖于消息(例如JMS、AMQP、MSMQ)协议 SOAP作为主要的远程访问协议。根据SOA架构范围和规模,在异构服务之间使用六七种远程访问协议的情形也并不少见。
谢谢大家关注,转发,点赞和点在看。