微服务和SOA服务

2023-05-04,,

微服务和SOA都被认为是基于服务的架构,这意味着这两种架构模式都非常强调将“服务”作为其架构中的首要组件,用于实现各种功能(包括业务层面和非业务层面)。微服务和SOA是两种差异很大的架构模式,但是他们仍有一些相同的特征。

所有基于服务的架构的一个共性是他们一般都是分布式架构,也就是服务组件都是通过远程访问协议来实现的,例如REST、SOAP、AMQP、JMS、MSMQ、RMI或者.NET Remoting。相对于单体式架构和分层式架构,分布式架构有很多优势,包括可伸缩性、解耦能力以及对开发、测试和部署的可控性等。分布式架构中的组件更趋向于自包含,因此其变更管理和维护也更容易,从而使得相应的应用也更稳定,响应也更快。分布式架构也非常适用于各模块之间耦合度较低、更加模块化的应用。

在基于服务的架构语境中,“模块化”指的是将应用的各个部分分别封装为自包含的服务的做法。拆分后的每个服务都可以单独设计、开发、测试和部署,与其他组件或服务之间的依赖性很低甚至没有。模块化的架构还支持用重写的方式来维护组件的做法。随着业务增长,架构可以逐渐地、以很小的部件为单位进行重构或者替换。

缺点则是复杂性的增加和投入的增长。维护服务合约、选择正确的远程访问协议、处理不响应的或不可用的服务、加密远程服务和管理分布式事务,这些还只是构造基于服务的架构时许多复杂问题中的一部分。

服务合约

服务合约是服务提供者(通常是远程的)和使用者(客户)之间使用合约语言(XML、JSON、Java Object等)约定数据输入和数据输出的一份协议。创建和维护服务合约是一项困难的工作,不应该被轻视或者当作补充条款。因此,服务合约的议题在基于服务的架构设计中值得特殊关注。

在基于服务的架构中,存在两种服务合约模型可供使用:基于服务的合约和客户驱动的合约。两者之间的真正差别在于合作程度。在基于服务的合约中,服务是合约的唯一拥有者,一般可以在不考虑服务客户需求情况下演化或修改合约。这种模式强迫服务的所有客户都要接受新的服务合约变更,而不管客户是否需要这些新功能。

与之相反,客户驱动的合约所基于的是服务和与服务客户之间更为密切地合作的一种关系。在这种模型下,服务拥有者和客户有很强的合作(关系),因此任何服务合约变更会充分考虑客户的需求。采用这种模型时,服务(拥有者)一般需要了解客户是谁以及每个客户都是如何使用这些服务的。客户可以对服务合约随意提出变更建议,服务方则可以根据是否影响其他客户而自行决定是否采纳。理想情况下,服务的客户向服务拥有者发起修改建议和测试用例,测试被执行时可以监测该修改是否影响其他客户。开源工具如Pact和Pacto可以帮助维护和测试这类客户驱动的合约。

服务合约的上下文中另一个重要议题是合约的版本。我们必须接受这一现实——在服务和服务客户间实现绑定的合约终究是要变化的。改变程度和范围取决于这些变更如何影响每个客户,以及合约改变后服务的向后兼容性。

服务合约中变更合约需要注意的是:一定要从开始就制定一个明确的服务客户沟通策略,以便客户能够及时获知合约变更的信息,或者特定合约类型不再被支持的信息。很多情况下,因为内部/外部客户很多,这类沟通并不可行。这时候,一个集成中心(integration hub),或者说消息中间件,可以在服务的客户与服务之间建立抽象层,实行服务合约的转译。

服务可用性

服务可用性(Availability)和服务响应能力(Responsiveness)是基于服务的架构通常需要考虑的另外两个问题。这两者都涉及服务客户与远程服务之间通信的能力,但他们还是有着轻微不同的内涵,因此客户也会采用不同方式来解决这两个问题。

这两种错误条件造成的结果是一样的(服务请求无法被处理),但是处理方式是不同的。因为服务可用性跟服务的可连接性有关,因此客户能够做的并不多,只能要么多试几次,要么在可能的情况下把请求放入队列,以后再处理。

处理服务响应能力问题就更困难一些。请求成功地发送给服务之后,客户需要等待多久?是否服务仅仅是很慢,又或者服务端发生了什么事情导致无法发送响应?解决超时条件问题对于远程服务的可连接性来说是相当具有挑战性的。一种常见的做法是在有负载的情况下获得最长响应时间作为基准,然后在此基础上添加额外的时延以处理负载的波动。例如,假设运行某个基准测试时,某个特定服务请求的最大响应时间为2000毫秒。那么你可能需要将这个值乘以二,用以处理负载较高的状况,因此预期的超时时长应该是4000毫秒。

比较常见的一种解决方案是使用断路器模式(circuit breaker pattern)。如果服务不能及时做出响应或者干脆不响应,软件断路器将会起作用,提醒客户不必浪费时间等待超时事件发生。与物理断路器相比,软件断路器很不错的一点是在实现这种设计模式时,可以在服务端开始发送响应或者变得可用时对自己进行重置。软件断路器有不少开源实现,包括Netfix的Ribbon等。

在处理超时时长值的问题时,应该尽量避免使用全局的超时值来处理每个请求。恰恰相反,建议你基于上下文来定义超时时长,同时确保这些数值可以从外部通过配置来设置,这样你就可以在负载条件变化的情况下快速做出响应,而不用重建或者重新部署应用。另一种方案是在代码中使用“智能超时值(smart timeout values)”,以便在负载变化情况下自行调整超时值。例如,应用可以在高负载或者网络有问题的情况下自动增加超时时长值。当负载降低,响应时间变短后,应用可以重新计算平均响应时间,并对应地降低超时时长值。

安全性

在基于服务架构下,服务都是远程访问的,很重要的一点是需要确认给定的客户是否被授权访问某个特定服务。根据实际情况,服务的客户可能既需要被认证(authentication)也需要被授权(authorization)。认证是指服务客户是否可以连接到某个服务,一般是通过用户名和密码来建立登录凭证来进行认证。某些情况下,仅仅执行认证还不够:客户可以连接到某个服务并不意味着他可以访问服务所提供的所有功能。授权指的是某个服务客户是否被允许访问服务内部特定的业务功能项。

安全在早期SOA实现中是个大问题。原本被严格隔离的某个应用的功能突然被公开,从企业的全球各处都能访问到。这一变化所引发的问题促使我们换个角度思考,重新认识服务以及如何保护服务不会被不该访问的客户访问到。

对微服务而言,安全问题成为挑战主要是因为没有一个专门处理安全问题的中间件组件。相反地,每个服务必须各自处理安全性问题,或者在某些情况下需要增强API层以使之更加智能地处理应用的安全性问题。我看到的微服务中所实现的一个比较好的设计是将认证工作委托给一个独立的服务,而保留授权工作在微服务内部。尽管这一设计可以修改为将认证和授权都交由单独的服务来完成,我还是倾向于将授权包含在微服务自身当中。这样做可以避免与远程安全性服务之间的太多的不必要交互,还可以使得服务的上下文边界更加稳定,减少对外界的依赖。

事务

事务问题在SOA架构中更为普遍,因为与微服务架构不同,SOA架构中通常使用多个服务来完成一个业务请求。我将在对比架构特点一章的“服务编排”一节中详细讨论这个问题。

基于服务的架构一般更依赖于 BASE事务,而不是ACID事务。BASE事务一般包括基本的可用性、软性的状态和最终一致性(basic availability、soft state和eventual consistency)。分布式应用依赖BASE事务来追求数据库中的最终一致性而不是每个中间事务的一致性。一个典型的BASE事务的例子是往ATM机里存钱。当通过ATM机向你的账户中存入现金,大概几分钟甚至几个小时后才会在账号中显现出来。换句话说,钱从离开自己的手到真正存入账户之间有一个软性的转换状态:钱已经离手,但是还没到达你的银行账户中。我们可以容忍这一延迟,并且寄希望于软状态和最终一致性,因为我们知道并信任这笔钱最终一定会到达我们的账号。从全系统视图的角度看,批处理作业也是依赖最终一致性的。

迁移到到基于服务的架构需要我们改变对事务和一致性的认识。对于不能依赖最终一致性和软状态的而必需事务一致性的情况,可以将服务的粒度设计得较粗,从而把业务逻辑包装在一个服务内,最后通过ACID事务来实现事务级别的一致性。另一种方法是使用事件驱动技术,当请求状态变得一致时向相关客户推送通知。这种技术给应用带来了很高的复杂度,不过确实能够在使用BASE事务时实现事务状态管理。

服务分类学

服务分类学指的是在某种架构下服务是如何归类的。有两种服务分类的基本类型:服务类型和业务领域。服务类型分类法会根据整个架构中服务所扮演的角色进行分类。例如,某些服务是实现业务功能的,而另一些服务可能是实现非业务功能的,例如日志、审计和安全。业务领域分类法会根据服务在特定业务功能领域中所扮演的角色来进行分类,例如报表、交易处理和订单送货等等。

服务类型分类一般在架构模式层进行定义,而业务领域分类则在架构实现层进行定义。尽管架构模式提供了很好的基础来定义服务类型,作为一个架构师,你可以按照自己的想法对其进行修改,定义自己的分类方法。本节中,我们会关注架构模式以及在微服务和SOA中比较常见的服务类型。

微服务架构就服务类型而言其分类法并不复杂,一般来说主要有两类服务类型,如图2-1所示。功能服务(functional services)是指用来支持特定业务操作或功能的服务,而基础服务(infrastructure services)则负责支持非业务工作,例如认证、授权、审计、日志和监控。在微服务架构中,这是非常重要的区别,因为基础服务并不对外开放而仅作为供内部使用的私有共享服务,对其它服务可用。功能服务则提供外部访问能力,而且不对其它服务共享。

SOA内的服务分类法跟微服务有很大不同。在SOA中,从全局架构来看有非常明确的、非常正式的服务类型,各自在整体架构中扮演不同角色。尽管在SOA中可以有任意数量的服务类型,架构模式定义了四种基本类型,如图所示:

业务服务(business services)是一种抽象的、高层级的、粗粒度的服务,定义在企业层面的执行的核心业务操作。因为抽象,所以不依赖于任何实现或者协议,一般只包括服务名字,期望的输入以及期望的输出。可选地,这些服务类型还可以包括处理步骤或者跟服务相关的特殊编排规则。业务服务一般都用XML、Web Services Definition Language(WSDL)或者Business Process Execution Language(BPEL)等语言来表述。一般确认某个服务是否属于业务服务会在服务名上下文前后加上“我们是否在做某某的业务”来加以判断。例如,有两个服务,分别名为ProcessTrade(处理交易)和InsertCustomer(插入客户)。那么“我们是否在做处理交易的业务”可以很清楚看出ProcessTrade是一个业务服务;而“我们是否在处理插入客户的业务”听上去就不对,所以不是一个好的业务服务抽象,更像是一个在处理业务服务时所调用的某个具体服务。

企业服务(enterprise services)是具体的、企业层级的、粗粒度的服务,用以实现业务服务所定义的功能。如图中所示,一般是介于抽象业务服务和对应具体企业服务实现之间的中间件,在其间起到桥梁作用。企业服务可以与业务服务之间存在一对一或一对多的对应关系。

它们可以用任何语言和平台进行定制,或者采用第三方采购的产品(COTS)来实现。企业服务很独特的一点是它们通常会在组织内共享。例如,一个RetrieveCustomer(检索客户)的企业服务可能被组织内很多模块使用,用来接收客户信息。其它例如CheckTradeCompliance(检查交易合规) , CreateCustomer(创建客户), ValidateOrder(验证订单) 和 GetInventory(获取库存目录)等都是企业服务很好的例子。企业服务通常依赖应用服务(application services)和基础服务(infrastructure services)来完成特定业务请求。但是在某些情况下,某个企业服务也可能把完成特定请求所需要的所有业务功能都归入自身,形成自包含的服务。

应用服务(application services)是细粒度的、特定于具体应用的服务,与某个特定应用的语境相关。应用服务提供在企业服务中没有的特定的业务功能。例如,一个大型保险公司汽车报价应用可能提供服务来计算汽车保险费率。这是一个只针对该应用而并不适用于整个企业的服务。应用服务可以从某个专用的用户界面直接调用,或者通过某个企业服务调用。应用服务的例子包括:AddDriver(添加司机)、AddVehicle(添加车辆)以及CalculateAutoQuote(计算机车报价)等等。

SOA中最后一个基本服务类型是基础服务(infrastructure services)。与微服务架构相同,这些服务用于实现非功能性任务,例如审计、安全和日志。在SOA中,基础服务可以从应用服务或者企业服务调用。

微服务和SOA服务的相关教程结束。

《微服务和SOA服务.doc》

下载本文的Word格式文档,以方便收藏与打印。