架构模式

本文翻译自Software Architecture Patterns

Layered Architecture

分层架构是最通用和使用频率最高的架构模式,通常也称之为N-层架构。这种模式也是大多数Java EE应用程序的事实上的架构标准,因此,它被大多数架构师,设计人员和开发人员所广泛了解。 分层架构模式能与传统的IT通信和大多数公司中的组织结构紧密匹配,这使其成为大多数业务应用程序开发工作的自然选择。

Pattern Description

分层架构模式中的组件被组织为水平分隔的层,每一层在应用程序中扮演特定的角色(例如,表示层或业务逻辑层)。 尽管分层体系架构模式没有指定模式中必须存在的层的数量和类型,但是大多数分层架构都由四个标准层组成:表示层业务层持久性数据库层,如图(图1-1)所示。在某些情况下,尤其是在将持久性逻辑(例如SQL或HSQL)嵌入业务层组件中时,业务层和持久性层会组合为一个业务层。 因此,较小的应用程序可能仅具有三层,而较大且更复杂的业务应用程序可能包含五层或更多层。

分层体系结构模式的每一层在应用程序中都有特定的角色和职责。例如,表示层将负责处理所有用户界面和浏览器通信逻辑,而业务层将负责执行与请求关联的特定业务规则。架构中的每个层围绕需要完成的工作形成抽象,以满足特定的业务需求。例如,表示层无需了解或担心如何获取客户数据;它只需要以特定格式在屏幕上显示该信息。同样,业务层无需担心如何格式化客户数据以在屏幕上显示,甚至客户数据来自何处;它只需要从持久层获取数据,对数据执行业务逻辑(例如,计算值或汇总数据),然后将该信息传递到表示层。

Figure 1-1. Layered architecture pattern

分层体系结构模式的强大功能之一是组件之间关注点的分离。 特定层中的组件仅处理与该层有关的逻辑。 例如,表示层中的组件仅处理表示逻辑,而驻留在业务层中的组件仅处理业务逻辑。 这种类型的组件分类使你可以轻松地在体系结构中建立有效的角色和责任模型,并且由于定义良好的组件接口和有限的组件范围,因此可以使用此体系结构模式轻松开发,测试,管理和维护应用程序。

Key Concepts

请注意,在图1-2中,体系结构中的每个层都标记为已关闭。 在分层架构模式中,这是一个非常重要的概念。 封闭层意味着当请求不能跨层。 例如,来自表示层的请求必须先经过业务层,然后再到达持久层,然后才到达数据库层。

Figure 1-2. Closed layers and request access

那么,为什么不允许表示层直接访问持久层或数据库层呢? 毕竟,从表示层进行直接数据库访问要比经过一堆不必要的层要快得多。 这个问题的答案在于一个关键概念,即层隔离。

层隔离意味着在架构的任意一层中进行的更改通常不会影响其它层中的组件:更改仅与该层中的组件相关。 如果允许表示层直接访问持久层,则在持久层中对SQL所做的更改将影响业务层和表示层,从而产生一个紧密耦合的应用程序,在组件之间具有许多相互依赖关系。 这种类型的体系结构变得非常困难且更改成本很高。

层隔离也意味着每一层都独立于其他层,因此对体系结构中其它层的内部运作了解甚少或根本不了解。 为了理解此概念的强大功能和重要性,请考虑进行大量的重构工作,以将表示框架从JSP(Java服务器页面)转换为JSF(Java Server Faces)。 假设表示层和业务层之间使用的合同(例如,模型)保持不变,则业务层不受重新引用的影响,并且完全独立于表示层使用的用户界面框架的类型 。

虽然封闭的层有助于层隔离,因此有助于隔离架构中的更改,但有时某些层是开放的是有意义的。 例如,
假设你想在架构中添加一个包含公共服务组件的层,该层需要被业务逻辑层访问。那么通常一个好的实践是添加一个服务层。因为架构体系限制了对共享服务层的访问。如果没有一个单独的层来表示该感念,那么就没有一个规则来限制对共享服务的访问。

在此示例中,新服务层可能会驻留在业务层下方,以指示无法从表示层访问此服务层中的组件。 但是,这带来了一个问题,即现在需要业务层经过服务层才能到达持久层,这根本没有意义。 这是分层体系结构中的一个古老问题,可以通过在体系结构中创建开放层来解决。

如图1-3所示,在这种情况下,服务层被标记为“开放”,这意味着允许请求绕过该开放层并直接进入其下一层。 在下面的示例中,由于服务层是开放的,因此现在允许业务层绕过它,而直接进入持久层,这是很有意义的。

Figure 1-3. Open layers and request flow

利用开放层和封闭层的概念可以帮助定义架构中层和请求流之间的关系,还可以为设计人员和开发人员提供必要的信息,以了解架构中各个层的访问限制。 无法记录或正确传达架构中哪些层是打开和关闭的(以及原因)通常会导致紧密耦合且脆弱的架构,这很难测试,维护和部署。

Pattern Example

为了说明分层架构是如何工作的,下面以一个获取客户信息的用例来进行具体说明,如图1-4所示。 黑色箭头表示直到数据库以检索客户数据的请求,红色箭返回到屏幕的数据响应。 在此示例中,客户信息包含客户数据和订单数据(客户下的订单)。

客户屏幕负责接受请求并显示客户信息。它不知道数据在哪里,如何检索它,或者必须查询多少个数据库表才能获取数据。客户屏幕一旦收到获取特定个人的客户信息的请求,便会将请求转发到客户委托模块。该模块负责了解业务层中的哪些模块可以处理该请求,以及如何到达该模块以及它需要哪些数据(合同)。业务层中的客户对象负责汇总业务请求所需的所有信息(在这种情况下,是获取客户信息)。此模块调出持久性层中的customer dao(数据访问对象)模块以获取客户数据,还调用order dao模块以获取订单信息。这些模块依次执行SQL语句以检索相应的数据,并将其传递回业务层中的客户对象。一旦客户对象接收到数据,它将汇总数据并将信息传递回客户委托人,然后由客户委托人将数据传递给客户屏幕以呈现给用户。

Figure 1-4. Layered architecture example

从技术角度来看,这些模块可以通过多种方式实现。 例如,在Java平台中,客户屏幕可以是(JSF)Java Server Faces。 业务层中的客户对象可以是本地Spring Bean或远程EJB3 Bean。 上一个示例中说明的数据访问对象可以实现为简单的POJO(普通的Java对象),MyBatis XML Mapper文件,甚至可以封装原始JDBC调用或Hibernate查询的对象。 从Microsoft平台的角度来看,客户屏幕可以是使用.NET框架访问业务层中C#模块的ASP(活动服务器页面)模块,而客户和订单数据访问模块则实现为ADO(ActiveX) 数据对象)。

Considerations

分层体系结构模式是可靠的,通用的架构模式,是大多数应用程序的良好起点,尤其是当你不确定哪种体系结构模式最适合您的应用程序时。但是,从系统架构的角度来看,选择此模式时需要考虑几件事。

首先要注意的是所谓的“体系结构坑洞反模式”。此反模式将请求流过架构的多个层的情况描述为简单的直通处理,而在每个层中执行的逻辑很少或没有执行逻辑。例如,假设表示层响应来自用户的请求以检索客户数据。表示层将请求传递给业务层,业务层仅将请求传递给持久层,然后持久层对数据库层进行简单的SQL调用以检索客户数据。然后,数据将一直传递到整个堆栈,而无需其他处理或逻辑来聚合,计算或转换数据。

每个分层架构中都或多或少包含一些反模式。但是,关键是分析属于反模式的请求的百分比。 通常,遵循8/2原则是确定你是否正在经历架构下陷反模式的好习惯。 通常,将大约20%的请求作为简单的直通处理进行处理,并将80%的请求具有与该请求相关联的某些业务逻辑。 但是,如果你发现真实的请求比例于此相反,并且大多数请求都是简单的直通处理,则可能要考虑使分层架构中的某些层打开,请记住,控制变更将更加困难 由于缺乏层隔离。

Pattern Analysis

下表是对分层架构模式的通用体系结构特征的评估和分析。 对每个特性的评级都基于该特性的自然趋势,即基于模式的典型实现以及该模式通常所知的能力。 有关此模式与本报告中其他模式的关系的并行比较,请参阅本报告末尾的附录A。

Overall agility

评分:低
分析:整体敏捷性是对不断变化的环境做出快速响应的能力。 虽然可以通过此模式可以将每个层的修改进行隔离,但是发现现实中,模式的大多数该实现中,组件都会紧密耦合,因此在该架构模式中进行更改仍然很麻烦且耗时。

Ease of deployment

评分:低
分析:根据你实施此模式的方式,部署可能会成为一个问题,尤其是对于大型应用程序。 对组件的一小处更改可能需要重新部署整个应用程序(或应用程序的很大一部分),从而导致需要在下班时间或周末计划,安排和执行部署。 因此,这种模式不容易使自己适应连续的交付管道,从而进一步降低了部署的总体评级。

Testability

评分:高
分析:由于组件属于架构中特定的层,因此可以对其他层进行Mock或打桩处理,使得这种模式相对易于测试。 开发人员可以模拟演示组件或屏幕以隔离业务组件中的测试,也可以模拟业务层以测试某些屏幕功能。

Performance

评分:低
分析:虽然某些分层架构确实可以很好的工作。但是由于几乎所有请求必须穿过多个层级,导致效率低下,因此该模式不适用于高性能应用程序。

Scalability

评分:低
分析:由于这种模式趋向于紧密耦合和整体实现的趋势,使用这种架构模式构建的应用程序通常很难扩展。你可以通过将层拆分为单独的物理部署或将整个应用程序复制到多个节点来扩展分层体系结构,但是总体而言,粒度太宽泛,因此扩展成本很高。

Ease of development

评分:高
分析:易于开发获得了相对较高的分数,主要是因为这种模式众所周知,并且实施起来并不太复杂。 因为大多数公司通过将技能集按层次(表示,业务,数据库)分开来开发应用程序,所以这种模式成为大多数业务应用程序开发的自然选择。 概述了公司的沟通和组织结构与软件开发方式之间的联系,这就是所谓的康韦定律。 您可以使用Google的“康韦定律”获取有关这种引人入胜的相关性的更多信息。

Event-Driven-Architecture

事件驱动的架构模式是一种流行的分布式异步架构模式,用于构建高度可伸缩的应用程序。 它还具有很高的适应性,可用于小型应用程序以及大型,复杂的应用程序。 事件驱动架构由高度架构,职责单一的事件处理组件组成,这些组件异步接收和处理事件。

事件驱动的架构模式由两个主要拓扑组成,即调解器和代理。 当你需要通过中央调解器协调事件中的多个步骤时,通常使用调解器拓扑结构;而当你希望不使用中央调解器将事件链接在一起时,则使用代理拓扑。 由于这两种拓扑的体系结构特征和实施策略不同,因此重要的是要了解每个拓扑,以了解哪种拓扑最适合您的特定情况。

Mediator Topology

调解器拓扑结构对于具有多个步骤并且需要某种编排级别的事件才能处理的事件很有用。 例如,进行一次股票交易的单个事件可能需要你首先验证交易,然后根据各种合规性规则检查该股票交易的合规性,将交易分配给经纪人,计算佣金,最后放置 与那个经纪人交易。 所有这些步骤都需要一定程度的编排,以确定步骤的顺序以及哪些步骤可以串行和并行完成。

调解器拓扑中有四种主要类型的体系结构组件:事件队列,事件调解器,事件通道和事件处理器。 事件流从客户端将事件发送到事件队列开始,该事件队列用于将事件传输到事件调解器。 事件调解器接收初始事件,并通过将其他异步事件发送到事件通道以执行流程的每个步骤来协调该事件。 监听事件通道的事件进程从事件调解器接收事件,并执行特定的业务逻辑来处理事件。 图2-1说明了事件驱动的体系结构模式的一般介体拓扑。

Figure 2-1. Event-driven architecture mediator topology

在事件驱动架构中,通常有十几到数百个事件队列。 该模式未指定事件队列组件的实现。 它可以是消息队列,Web服务终结点或其任何组合。

在该模式中有两种类型的事件:初始事件和处理事件。 初始事件是调解器接收到的原始事件,而处理事件是由调解器生成并由事件处理组件接收的事件。

事件调解器组件负责协调初始事件中包含的步骤。 对于初始事件中的每个步骤,事件调解器都会将特定的处理事件发送到事件通道,然后由事件处理器进行接收和处理。 需要注意的是,事件调解器实际上并未执行处理初始事件所需的业务逻辑; 相反,它知道处理初始事件所需的步骤。

事件调解器使用事件通道将与初始事件中每个步骤相关的特定处理事件异步传递给事件处理器。 事件通道可以是消息队列,也可以是消息主题,尽管消息主题在调解器拓扑结构中使用最广泛,所以处理事件可以由多个事件处理器处理(每个事件处理器根据接收到的处理事件执行不同的任务)。

事件处理器组件包含处理处理事件所需的应用程序业务逻辑。 事件处理器是独立的,高度分离的架构组件,它们在应用程序或系统中执行特定任务。 尽管事件处理程序组件的粒度可以从细粒度(例如,计算订单的销售税)到粗粒度(例如,处理保险索赔),但重要的是要记住,每个事件处理器组件应执行单个业务任务,而不要依赖其他事件处理器来完成其特定任务。

事件中介器可以通过多种方式实现。 作为架构师,你应该了解所有这些实现选项,以确保为事件介体选择的解决方案符合您的需求。

事件中介器的最简单,最常见的实现方法是通过开源集成中心,例如Spring Integration,Apache Camel或Mule ESB。 这些开源集成中心中的事件流通常通过Java代码或DSL(领域特定语言)实现。 对于更复杂的中介和业务流程,可以将BPEL(业务流程执行语言)与BPEL引擎(例如开源Apache ODE)结合使用。 BPEL是一种类似于XML的标准语言,它描述了处理初始事件所需的数据和步骤。 对于需要更复杂的业务流程的大型应用程序(包括涉及人机交互的步骤),您可以使用业务流程管理器(BPM)(例如jBPM)来实现事件中介程序。

了解你的需求并将其与正确的事件中介器实现相匹配对于使用此拓扑的任何事件驱动的架构的成功至关重要。 与实施BPM解决方案以执行简单的路由逻辑一样,使用开源的集成中心进行非常复杂的业务流程管理流程是失败的秘诀。

为了说明调解器拓扑的工作原理,假设你已通过一家保险公司投保,并且决定搬家。 在这种情况下,初始事件可能称为重定位事件。 处理重定位事件所涉及的步骤包含在事件中介器中,如图2-2所示。 对于每个初始事件步骤,事件介体创建一个处理事件(例如,更改地址,重新计算报价等),将该处理事件发送到事件通道,并等待该处理事件被相应的事件处理器处理(例如, ,客户流程,报价流程等)。 该过程一直持续到初始事件中的所有步骤都已处理完毕。 事件调解器中重新计算报价和更新声明步骤上的单条表示可以同时运行这些步骤。

Broker Topology

代理拓扑与中介器拓扑的不同之处在于,不存在中央事件中介器。 而是通过轻量级消息代理(例如ActiveMQ,HornetQ等)以链状方式将消息流分布在事件处理器组件之间。 当你具有相对简单的事件处理流程并且不需要(或不需要)集中事件编排时,此拓扑很有用。

代理拓扑中有两种主要类型的体系结构组件:代理组件和事件处理器组件。 代理组件可以是集中式的或联合的,并包含事件流中使用的所有事件通道。

代理组件中包含的事件通道可以是消息队列,消息主题或两者的组合。

Figure 2-2. Mediator topology example

这种拓扑如图2-3所示。 从图中可以看出,没有中央事件中介器组件来控制和协调初始事件。 相反,每个事件处理器组件负责处理一个事件并发布一个新事件,以指示其刚刚执行的操作。 例如,平衡股票投资组合的事件处理器可能会收到一个称为股票分割的初始事件。 基于该初始事件,事件处理器可以进行一些投资组合重新平衡,然后将一个新事件发布到称为重新平衡投资组合的经纪人,然后由另一个事件处理器处理该新事件。 请注意,有时某个事件是由事件处理器发布但未被其他任何事件处理器处理的。 在开发应用程序或提供将来的功能和扩展时,这很常见。

Figure 2-3. Event-driven architecture broker topology

为了说明代理拓扑的工作方式,我们将使用与调解器拓扑相同的示例(被保险人移动)。由于没有中间事件中介程序可以在代理拓扑中接收初始事件,因此客户流程组件可以直接接收该事件,更改客户地址并发送事件,说明它更改了客户地址(例如,更改地址事件) 。在此示例中,有两个对更改地址事件感兴趣的事件处理器:报价过程和声明过程。报价处理器组件根据地址更改重新计算新的自动保险费率,并将一个事件发布到系统的其余部分,以指示其所做的事情(例如,重新计算报价事件)。另一方面,理赔处理组件接收相同的变更地址事件,但是在这种情况下,它会更新未完成的保险理赔并将事件作为更新理赔事件发布到系统。然后,这些新事件将由其他事件处理器组件接收,并且事件链会贯穿整个系统,直到不再有针对该特定发起事件的事件发布为止。

Figure 2-4. Broker topology example

从图2-4中可以看到,代理拓扑完全是有关事件链以执行业务功能的。 理解代理拓扑的最好方法是将其视为中继竞赛。 在接力赛中,跑步者握住警棍并跑一定距离,然后将警棍交给下一位跑步者,依此类推,直到最后一名选手越过终点线。 在接力赛中,一旦跑步者交出了接力棒,她就完成了比赛。 代理拓扑也是如此:一旦事件处理器移交该事件,该事件将不再参与该特定事件的处理。

Considerations

事件驱动的架构模式是一种相对复杂的实现方式,主要是由于其异步的分布式特性。 实施此模式时,必须解决各种分布式体系结构问题,例如远程进程可用性,缺乏响应能力以及在代理或中介程序发生故障的情况下代理重新连接逻辑。

选择这种体系结构模式时要考虑的一个考虑因素是单个业务流程缺乏原子事务。由于事件处理器组件高度分离和分布,因此很难在它们之间维护事务性工作单元。因此,在使用这种模式设计应用程序时,您必须不断考虑哪些事件可以独立运行,也可以不独立运行,并相应地计划事件处理器的粒度。如果发现需要在事件处理器之间划分单个工作单元,也就是说,如果您对应该是不可分割事务的事物使用单独的处理器,那么这可能不是适合您的应用程序的模式。

事件驱动的架构模式中最困难的方面之一可能是事件处理器组件合同的创建,维护和治理。每个事件通常都有一个与之相关的特定协定(例如,将数据值和数据格式传递给事件处理器)。使用此模式来建立标准数据格式(例如XML,JSON,Java对象等)并从一开始就制定合同版本控制政策时,这一点至关重要。

Pattern Analysis

下表包含事件驱动架构模式的通用架构特征的评级和分析。 对每个特性的评级都基于该特性的自然趋势,即基于模式的典型实现以及该模式通常所知的能力。 有关此模式与本报告中其他模式的关系的并行比较,请参阅本报告末尾的附录A。

Overall agility

评分:高
分析:整体敏捷性是对不断变化的环境做出快速响应的能力。 由于事件处理器组件是单一用途的,并且与其他事件处理器组件完全脱钩,因此更改通常隔离到一个或几个事件处理器,并且可以快速进行而不会影响其他组件。

Ease of deployment

评分:高
分析:总体而言,由于事件处理器组件的分离性质,此模式相对易于部署。 代理拓扑往往比事件中介拓扑更易于部署,主要是因为事件介体组件在某种程度上与事件处理器紧密耦合:事件处理器组件的更改也可能需要事件介体的更改,这需要同时 部署用于任何给定的更改。

Testability

评分:低
分析:尽管单个单元测试不是很困难,但是它确实需要某种专门的测试客户端或测试工具来生成事件。 这种模式的异步特性也使测试变得复杂。

Performance

评分:高
分析:虽然由于所有消息传递基础架构的原因,可能实现一个性能不佳的事件驱动架构是可能的,但通常,该模式通过其异步功能可实现高性能; 换句话说,执行解耦,并行异步操作的能力超过了对消息进行排队和出队的成本。

Scalability

评分:高
分析:通过高度独立和解耦的事件处理器,自然可以在这种模式下实现可伸缩性。 每个事件处理器可以分别缩放,以实现细粒度的可伸缩性。

Ease of development

评分:低
分析:由于模式的异步性质以及合同创建,并且对于无响应的事件处理器和失败的代理程序,代码中需要更高级的错误处理条件,因此开发可能会有些复杂。

Microkernel Architecture

微内核架构模式(有时也称为插件架构模式)是用于实现基于产品的应用程序的自然模式。 基于产品的应用程序是一种打包的应用程序,可以作为典型的第三方产品的版本进行下载。也有许多公司的业务应用也遵循这种模式,开发,发布版本,附带版本说明,版本新增的特性。微内核架构模式允许你将其他应用程序功能作为插件添加到核心应用程序,从而提供可扩展性以及功能分离和隔离。

Pattern Description

微内核架构模式由两个架构组件组成:核心系统 和 插件模块。应用的逻辑被分隔到每个相互独立的插件和基础的系统核心上。应用程序具有可扩展性,灵活性,并且应用的功能和特殊的业务逻辑具备隔离性。如图3-1,展示了微内核架构模式。

从业务应用程序的角度来看,核心系统通常定义为通用业务逻辑,没有特殊情况,特殊规则或复杂的条件处理的自定义代码。

插件模块是独立的独立组件,包含专门的处理,附加功能和自定义代码,旨在增强或扩展核心系统以产生更多的业务功能。通常,插件模块应独立于其他插件模块,但是你当然可以设计需要依赖其他插件的插件。无论哪种方式,都必须将插件之间的通信保持在最小限度,以避免依赖关系。

核心系统需要知道哪些插件模块可用以及如何获得它们。一种通用的实现方法是通过某种插件注册表。该注册表包含有关每个插件模块的信息,包括其名称,数据约定和远程访问协议详细信息(取决于插件如何连接到核心系统)。例如,标记高风险税收审核项目的税收软件插件可能具有一个注册表项,其中包含服务名称(AuditChecker),数据合同(输入数据和输出数据)以及合同格式(XML)。如果通过SOAP访问该插件,则它也可能包含WSDL(Web服务定义语言)。

插件模块可以通过多种方式连接到核心系统,包括OSGi(开放服务网关倡议),消息传递,Web服务,甚至直接点对点绑定(即对象实例化)。你使用的连接类型取决于你正在构建的应用程序类型(小型产品或大型业务应用程序)以及你的特定需求(例如,单部署或分布式部署)。架构模式本身没有指定任何这些实现​​细节,只是插件模块必须彼此独立。

插件模块和核心系统之间的合同范围很广,从标准合同到定制合同都可以。自定义合同通常在第三方开发插件组件的情况下发现,而您无法控制插件使用的合同。在这种情况下,通常在插件触点和您的标准合同之间创建一个适配器,这样核心系统就不需要每个插件的专门代码。创建标准合同(通常通过XML或Java Map实现)时,切记要从头开始创建版本控制策略,这一点很重要。

Pattern Examples

也许微内核体系结构的最佳示例是Eclipse IDE。下载基本的Eclipse产品仅能为您提供精美的编辑器。但是,一旦开始添加插件,它将成为高度可定制和有用的产品。互联网浏览器是使用微内核架构的另一个常见产品示例:查看器和其他插件添加了其他功能,而这些功能是基本浏览器(即核心系统)中找不到的。

对于基于产品的软件,示例无穷无尽,但是大型企业应用程序呢?微内核架构也适用于这些情况。为了说明这一点,让我们使用另一个保险公司示例,但这一次涉及保险索赔处理。

索赔处理是一个非常复杂的过程。每个州对于保险索赔中允许和不允许的内容都有不同的规则和规定。例如,如果您的挡风玻璃被一块岩石损坏,某些州允许免费更换挡风玻璃,而其他州则不允许。这为标准索赔流程创造了几乎无限的条件。

毫不奇怪,大多数保险索赔应用程序利用大型而复杂的规则引擎来处理这种复杂性中的大部分。 但是,这些规则引擎可能会变成一个复杂的大团团,在其中更改一个规则会影响其他规则,或者使简单的规则更改需要大量的分析人员,开发人员和测试人员。 使用微内核架构模式可以解决许多这些问题。

如在图3-2中看到的文件夹堆栈代表了索赔处理的核心系统。 它包含保险公司处理索赔所需的基本业务逻辑,除非没有任何自定义处理。 每个插件模块都包含该状态的特定规则。 在此示例中,可以使用自定义源代码或单独的规则引擎实例来实现插件模块。 无论采用哪种实施方式,关键点是特定于状态的规则和处理与核心声明系统是分开的,可以添加,删除和更改,而对其余核心系统或其他插件模块几乎没有影响 。

Considerations

关于微内核架构模式的一个很棒的事情是它可以被嵌入到应用中或用作另一种架构模式的一部分。 例如,如果此模式解决了应用程序特定易失区域的特定问题,则可能会发现你无法使用此模式实现整个体系结构。 在这种情况下,你可以将微服务架构模式嵌入到你正在使用的其他模式(例如分层架构)中。 类似地,可以使用微服务架构模式来实现上一节中有关事件驱动架构的事件处理器组件。

微服务架构模式为演化设计和增量开发提供了强大的支持。 你可以首先创建一个坚实的核心系统,然后随着应用程序的逐步发展,无需对核心系统进行重大更改即可添加功能。

对于基于产品的应用程序,微内核架构模式应始终是你的初始架构的首选,尤其是对于那些随着时间的推移将发布其他功能并希望控制哪些用户获得哪些功能的产品。 如果随着时间的流逝,你发现该模式不能满足您的所有需求,那么你始终可以将你的应用程序重构为更适合你特定需求的另一种架构模式。

Pattern Analysis

下表包含事件驱动架构模式的通用架构特征的评级和分析。 对每个特性的评级都基于该特性的自然趋势,即基于模式的典型实现以及该模式通常所知的能力。 有关此模式与本报告中其他模式的关系的并行比较,请参阅本报告末尾的附录A。

Overall agility

评分:高
分析:整体敏捷性是对不断变化的环境做出快速响应的能力。 通过松散耦合的插件模块,可以很大程度上隔离和快速实现更改。 通常,大多数微内核体系结构的核心系统趋向于快速稳定,因此相当健壮,并且随着时间的推移几乎不需要更改。

Ease of deployment

评分:高
分析:根据模式的实现方式,可以在运行时(例如,热部署)将插件模块动态添加到核心系统,从而最大程度地减少部署期间的停机时间。

Testability

评分:高
分析:插件模块可以单独进行测试,并且可以由核心系统轻松模拟,以演示或原型化特定功能,而对核心系统的更改很少或没有更改。

Performance

评分:高
分析:虽然微内核模式无法自然地适用于高性能应用程序,但是通常来说,大多数使用微内核架构模式构建的应用程序都表现良好,因为你可以自定义和简化应用程序以仅包含所需的那些功能。 JBoss应用服务器就是一个很好的例子:借助其插件体系结构,你可以对应用服务器进行剪裁,只保留需要的功能,删除哪些消耗CPU,内存而且用不上的功能,例如远程访问,消息传递和消耗内存的缓存。

Scalability

评分:低
分析:由于大多数微内核体系结构实现都是基于产品的,并且通常尺寸较小,因此它们被实现为单个单元,因此无法高度扩展。 根据实现插件模块的方式,有时可以在插件功能级别提供可伸缩性,但是总的来说,这种模式因产生高度可扩展的应用程序而闻名。

Ease of development

评分:低
分析:微内核架构模式需要仔细设计和契约治理,因此实施起来相当复杂。 契约版本控制,内部插件注册中心,插件粒度以及可用于插件连接的广泛选择都增加了实现此模式所涉及的复杂性。

Microservices Architecture Pattern

作为单体应用程序和SOA架构模式的可行替代方案,微服务体系结构模式正在业界迅速普及。 由于这种架构模式仍在不断发展,因此业界对这种模式的意义以及实现方式存在很多困惑。 该报告的这一部分将为你提供必要的关键概念和基础知识,以了解这种重要架构模式的好处(以及权衡取舍),以及它是否适合您的应用程序。

Pattern Description

无论选择哪种拓扑或实现方式,都有几个通用的核心概念适用于常规体系结构模式。 这些概念中的第一个就是独立部署的单元。 如图4-1所示,微服务体系结构的每个组件都作为一个单独的单元进行部署,从而可以通过有效且简化的交付管道,更强的可伸缩性以及应用程序内部组件高度耦来简化应用程序部署。

理解微服务架构模式,最重要的概念就是好服务组件。与其考虑微服务架构中的服务,不如考虑服务组件,服务组件的粒度可以是单个模块或整个应用的大部分功能。 服务组件包含一个或多个模块(例如Java类),这些模块表示单一用途的功能(例如,为特定城市或城镇提供天气)或大型业务应用程序的独立部分(例如,股票交易) 放置位置或确定自动保险费率)。 设计合适的服务组件粒度是微服务体系结构中的最大挑战之一。 在以下服务组件编排小节中将更详细地讨论此挑战。

微服务架构模式中的另一个关键概念是分布式架构,这意味着架构中的所有组件都完全相互分离,并可以通过某种远程访问协议(例如JMS,AMQP,REST, SOAP,RMI等)。 这种体系结构模式的分布式性质是它如何实现其某些出色的可扩展性和部署特性。

微服务架构令人兴奋的事情之一是,它是从与其他常见架构模式相关的问题演变而来的,而不是作为等待问题发生的解决方案而创建的。 微服务架构样式自然是从两个主要来源演变而来:使用分层架构模式开发的单体应用程序和通过SOA架构开发的分布式应用程序。

从单体应用程序到微服务架构模式的演进路径主要是由持续交付的发展所推动的,即从开发到生产的持续部署管道的概念简化了应用程序的部署。 整体应用程序通常由紧密耦合的组件组成,这些组件是单个可部署单元的一部分,这使得它麻烦且难以更改,测试和部署应用程序(因此,通常在大型应用程序中通常会出现“每月部署”周期的增加) IT商店)。 这些因素通常导致脆弱的应用程序在每次部署新内容时都会中断。 微服务架构模式通过将应用程序分为多个可部署单元(服务组件)来解决这些问题,这些可部署单元可以独立于其他服务组件进行单独开发,测试和部署。

导致微服务架构模式的另一条演进路径是来自实现面向服务的架构模式(SOA)的应用程序发现的问题。 尽管SOA模式非常强大,并提供无与伦比的抽象水平,异构连接,服务编排以及使业务目标与IT功能保持一致的希望,但它却是复杂,昂贵,无处不在,难以理解和实现的, 对于大多数应用程序来说通常是多余的。 微服务架构风格通过简化服务概念,消除编排需求以及简化连接和对服务组件的访问来解决此复杂性。

Pattern Topologies

尽管实际上有数十种方法来实现微服务架构模式,但三种主要的拓扑结构是最常见和流行的:基于API REST的拓扑,基于应用程序REST的拓扑和集中式消息传递拓扑。

基于API REST的拓扑对于通过某种API(应用程序编程接口)提供公开的,独立且自包含服务的小型网站很有用。这种拓扑结构如图4-2所示,由非常细粒度的服务组件(因此称为微服务)组成,这些服务组件包含一个或两个模块,这些模块执行独立于其余服务的特定业务功能。 在这种拓扑中,通常使用独立部署的WEB服务,基于REST api来访问这些细粒度的服务组件。 这种拓扑的示例包括Yahoo,Google和Amazon发现的一些常见的基于云的单一用途RESTful Web服务。

基于应用程序REST的拓扑与基于API REST的方法的不同之处在于,通过传统的基于Web或胖客户端的业务应用程序屏幕而不是通过简单的API层来接收客户端请求。 如图4-3所示,该应用程序的用户界面层被部署为一个单独的Web应用程序,可通过基于REST的简单接口远程访问单独部署的服务组件(业务功能)。 此拓扑中的服务组件与基于API-REST的拓扑中的服务组件的不同之处在于,这些服务组件往往更大,更粗糙,并且只占整个业务应用程序的一小部分,而不是细粒度的单项应用。 行动服务。 对于具有相对较低复杂度的中小型企业应用程序,此拓扑是常见的。

微服务架构模式中的另一种常见方法是集中式消息传递拓扑。 该拓扑(如图4-4所示)与以前的基于REST的应用程序拓扑类似,不同之处在于,该拓扑使用轻量级的集中消息代理(例如ActiveMQ,HornetQ等)代替了使用REST进行远程访问。 。 在查看此拓扑时,不要将其与面向服务的体系结构模式混淆或将其视为“ SOA-Lite”,这一点至关重要。在此拓扑中找到的轻量级消息代理不执行任何编排,转换或复杂的路由。 ;而是访问远程服务组件的轻量级传输。

集中式消息传递拓扑通常在较大的业务应用程序或需要在用户界面和服务组件之间的传输层上进行更复杂控制的应用程序中找到。 与以前讨论的基于REST的简单拓扑相比,此拓扑的优点是高级排队机制,异步消息传递,监视,错误处理以及更好的总体负载平衡和可伸缩性。 通常通过代理群集和代理联合来解决通常与集中式代理相关的单点故障和体系结构瓶颈问题(将单个代理实例拆分为多个代理实例,以根据系统功能区域划分消息吞吐量负载)。

Avoid Dependencies and Orchestration

微服务架构模式的主要挑战之一是为服务组件确定正确的粒度级别。 如果服务组件的粒度过于粗糙,你可能不会得到微服务架构模式所带来的好处(部署,可伸缩性,可测试性和松散耦合)。 但是,太细粒度的服务组件将导致对服务编排的要求,这将迅速将你的精益微服务架构转变为重量级的SOA架构,并通常会面临所有SOA架构中的复杂性:复杂性高,混乱,费用和不足。

如果发现需要从应用程序的用户界面或API层内部编排服务组件,则可能是服务组件的粒度太细。 同样,如果发现需要在服务组件之间执行服务间通信以处理单个请求,则从业务功能的角度来看,服务组件的粒度可能太细或分配不正确。

可以通过共享数据库来处理服务间通信,这可能会导致组件之间发生意外耦合。 例如,如果处理Internet订单的服务组件需要客户信息,则可以转到数据库以检索必要的数据,而不是调用客户服务组件中的功能。

共享数据库可以处理信息需求,但是共享功能呢? 如果服务组件需要另一个服务组件中包含的功能或所有服务组件都具有的功能,则有时您可以跨服务组件复制共享功能(从而违反了DRY原则:请勿重复自己的操作)。 在大多数实现微服务架构模式的业务应用程序中,这是相当普遍的做法,为了保持服务组件独立并分离其部署,需要折衷重复业务逻辑的小部分的冗余。 小型实用程序类可能属于此类重复代码。

如果您发现无论服务组件的粒度级别如何,您仍然无法避免对服务组件进行编排,那么这很好地表明这可能不是您应用程序的正确架构模式。 由于该模式具有分布式特性,因此很难在服务组件之间(以及之间)维护单个事务性工作单元。 这种做法需要某种事务补偿框架来回滚事务,这给这种相对简单而优雅的体系结构模式增加了极大的复杂性。

Considerations

微服务架构模式解决了在单片应用程序和面向服务的架构中发现的许多常见问题。 由于主要的应用程序组件被分解为较小的,单独部署的单元,因此使用微服务架构模式构建的应用程序通常更健壮,提供更好的可伸缩性并且可以更轻松地支持连续交付。

这种模式的另一个优点是,它提供了进行实时生产部署的能力,从而大大减少了对传统的每月或周末“大爆炸”生产部署的需求。 由于更改通常与特定服务组件隔离,因此仅需要部署更改的服务组件。 如果只有服务组件的单个实例,则可以在用户界面应用程序中编写专门的代码以检测活动的热部署并将用户重定向到错误页面或等待页面。 或者,您可以在实时部署期间内外交换服务组件的多个实例,从而在部署周期内保持连续可用性(这对于分层体系结构模式来说很难做到)。

需要考虑的最后一个考虑因素是,由于微服务架构模式是分布式架构,因此它也存在一些与事件驱动的架构模式相同的复杂问题,包括合同创建,维护和管理。 ,远程系统可用性以及远程访问身份验证和授权。

Pattern Analysis

下表包含事件驱动架构模式的通用架构特征的评级和分析。 对每个特性的评级都基于该特性的自然趋势,即基于模式的典型实现以及该模式通常所知的能力。 有关此模式与本报告中其他模式的关系的并行比较,请参阅本报告末尾的附录A。

Overall agility

评分:高
分析:整体敏捷性是对不断变化的环境做出快速响应的能力。 由于是分开部署的单元的概念,通常将更改隔离到单个服务组件中,从而可以快速轻松地进行部署。 同样,使用此模式构建的应用程序往往非常松散地耦合在一起,这也有助于促进更改。

Ease of deployment

评分:高
分析:由于每个服务的细粒度和独立性,微服务架构下的服务独立部署性很高。 服务通常作为单独的软件单元进行部署,从而能够在白天或晚上的任何时间进行“热部署”。 总体部署风险也大大降低了,因为失败的部署可以更快的回滚,并且仅影响和正常部署上线的服务有依赖关系的服务的操作,其他没有依赖关系的操作可以正常执行。

Testability

评分:高
分析:由于将业务功能分离和隔离在独立的应用程序中,因此可以对测试进行范围划分,从而可以进行更有针对性的测试工作。 与针对整个整体应用程序的回归测试相比,针对特定服务组件的回归测试要容易得多,也更可行。 而且,由于这种模式中的服务组件是松散耦合的,因此从开发的角度来看,进行更改的可能性较小,这会破坏应用程序的另一部分,从而减轻了必须测试整个应用程序的测试负担。 一个小的变化。

Performance

评分:低
分析:虽然你可以通过在此模式下创建和实现性能很好的应用程序,但是由于微服务架构模式的分布式性质,因此,总体而言,此模式并不自然地适合于高性能应用程序。

Scalability

评分:高
分析:由于将应用程序划分为单独部署的单元,因此每个服务组件都可以单独扩展,从而可以对应用程序进行微调。 例如,由于该功能的用户量少,可能不需要扩展该股票交易应用程序的管理区域,但是由于大多数交易应用程序为此需要高吞吐量,因此可能需要扩展该交易交易服务组件 功能。

Ease of development

评分:高
分析:由于功能被隔离到单独且不同的服务组件中,因此由于范围较小且隔离,开发变得更加容易。 开发人员对一个服务组件进行更改而影响其他服务组件的机会要少得多,从而减少了开发人员或开发团队之间所需的协调。

Space-Based Architecture

大多数基于Web的业务应用程序遵循相同的常规请求流:浏览器的请求命中Web服务器,然后是应用程序服务器,最后是数据库服务器。尽管此模式对一小部分用户适用,但随着用户负载的增加,瓶颈开始出现,首先在Web服务器层,然后在应用程序服务器层,最后在数据库服务器层。针对用户负载增加导致的瓶颈。通常的应对策略是扩展Web服务器。这是相对容易且便宜的,并且有时可以解决瓶颈问题。但是,在大多数用户负载较高的情况下,扩展Web服务器层只会将瓶颈移至应用程序服务器。扩展应用程序服务器可能比Web服务器更为复杂和昂贵,并且通常只是将瓶颈转移到数据库服务器上,而扩展规模甚至更加困难且昂贵。即使可以扩展数据库,最终最终还是一个三角形的拓扑,三角形的最宽部分是Web服务器(最容易扩展),最小的部分是数据库(最难扩展)。

在任何具有大量并发用户,高负载的应用程序中,数据库通常是你可以同时处理多少个事务的最终限制因素。 尽管各种缓存技术和数据库扩展产品有助于解决这些问题,但事实是,针对极端负载扩展正常应用程序是非常困难的。

基于空间的架构模式被设计用于专门解决应用的可伸缩性和并发性问题。 对于具有可变且不可预测的并发用户数量的应用程序,这也是一种有用的架构模式。 从结构上解决极端和可变的可伸缩性问题通常是比尝试扩展数据库或将缓存技术改造为不可扩展的体系结构更好的方法。

模式说明

基于空间的架构模式(有时也称为云架构模式)使限制应用程序扩展的因素最小化。该模式的名称来自元组空间的概念,即分布式共享内存的概念。通过消除中央数据库约束,并通过使用基于内存的数据网格来实现高可伸缩性。应用程序数据保存在内存中,并在所有活动处理单元之间复制。随着用户负载的增加和减少,处理单元可以动态启动和关闭,从而解决了可变的可扩展性。因为没有中央数据库,所以消除了数据库瓶颈,在应用程序中提供了几乎无限的可伸缩性。

符合此模式的大多数应用程序都是标准网站,这些网站从浏览器接收请求并执行某种操作。一个竞标拍卖网站就是一个很好的例子。该站点通过浏览器请求不断接收来自互联网用户的出价。该应用程序将接收特定项目的出价,记录带有时间戳的出价,并更新该项目的最新出价信息,然后将信息发送回浏览器。

该体系结构模式中有两个主要组件:处理单元和虚拟化中间件。 图5-1说明了基本的基于空间的架构模式及其主要架构组件。

处理单元组件包含应用程序组件(或应用程序组件的一部分)。 这包括基于Web的组件以及后端业务逻辑。 处理单元的内容根据应用程序的类型而有所不同-较小的基于Web的应用程序可能会部署到单个处理单元中,而较大的应用程序可能会基于应用程序将应用程序功能拆分为多个处理单元 应用程序的功能区域。 处理单元通常包含应用程序模块,以及内存中的数据网格和用于故障转移的可选异步持久性存储。 它还包含一个复制引擎,虚拟化的中间件使用该复制引擎将一个处理单元所做的数据更改复制到其他活动的处理单元。

Figure 5-1. Space-based architecture pattern

虚拟中间件组件处理内部管理和通信。 它包含控制数据同步和请求处理各个方面的组件。 虚拟化中间件中包括消息传递网格,数据网格,处理网格和Deployment Manager。 下一部分将详细介绍这些组件,这些组件可以定制编写或作为第三方产品购买。

Pattern Dynamics

基于空间的架构模式的神奇之处在于虚拟化的中间件组件和每个处理单元内包含的内存数据网格。 图5-2显示了典型的处理单元架构,其中包含应用程序模块,内存数据网格,用于故障转移的可选异步持久性存储以及数据复制引擎。

虚拟化的中间件实质上是体系结构的控制器,并管理请求,会话,数据复制,分布式请求处理和流程单元部署。 虚拟化中间件中有四个主要的体系结构组件:
消息传递网格,数据网格,处理网格和部署管理器。

Figure 5-2. Processing-unit component

Messaging Grid

图5-3中所示的消息传递网格管理输入请求和会话信息。 当请求进入虚拟化中间件组件时,消息传递网格组件确定哪些活动处理组件可用于接收请求,并将请求转发到那些处理单元之一。 消息传递网格的复杂性可以从简单的循环算法到更复杂的下一个可用算法,后者可以跟踪哪个处理单元正在处理哪个请求。

Data Grid

数据网格组件可能是此模式中最重要和至关重要的组件。 数据网格与每个处理单元中的数据复制引擎进行交互,以在发生数据更新时管理处理单元之间的数据复制。 由于消息传递网格可以将请求转发到任何可用的处理单元,因此每个处理单元在其内存数据网格中包含完全相同的数据至关重要。 尽管图5-4显示了处理单元之间的同步数据复制,但实际上这是异步并行且非常快速地完成的,有时会在几微秒(百万分之一秒)的时间内完成数据同步。

Figure 5-3. Messaging-grid component

Figure 5-4. data-grid component

Processing Grid

如图5-5所示,处理网格是虚拟化中间件中的可选组件,当存在多个处理单元(每个处理一部分应用程序)时,该组件可管理分布式请求处理。 如果有要求在处理单元类型(例如,订单处理单元和客户处理单元)之间进行协调的请求,则处理网格在这两个处理单元之间中介和协调请求。

Figure 5-5. Processing-grid component

Deployment Manager

部署管理器组件根据负载条件管理处理单元的动态启动和关闭。 该组件持续监视响应时间和用户负载,并在负载增加时启动新的处理单元,并在负载减少时关闭处理单元。 它是实现应用程序中可变的可伸缩性需求的关键组件。

Considerations

基于空间的体系结构模式是一种复杂且昂贵的实现模式。 对于负载可变的较小的基于Web的应用程序(例如社交媒体网站,竞标和拍卖网站),这是一个不错的体系结构选择。 但是,它不适用于具有大量操作数据的传统大规模关系数据库应用程序。

尽管基于空间的体系结构模式不需要集中的数据存储,但是通常会包含一个空间来执行初始内存数据网格加载并异步保存处理单元进行的数据更新。 创建单独的分区来隔离易失性和广泛使用的事务数据与非活动数据也是一种常见的做法,以减少每个处理单元中内存数据网格的内存占用。

重要的是要注意,虽然此模式的替代名称是基于云的体系结构,但处理单元(以及虚拟化中间件)不必驻留在基于云的托管服务或PaaS(平台作为 服务)。 它可以轻松地驻留在本地服务器上,这就是我更喜欢“基于空间的体系结构”的原因之一。
从产品实施的角度来看,您可以通过第三方产品(例如GemFire,JavaSpaces,GigaSpaces,IBM Object Grid,nCache和Oracle Coherence)以这种模式实施许多架构组件。 由于这种模式的实现在成本和功能(尤其是数据复制时间)方面差异很大,因此,作为一名架构师,您应该首先确定自己的特定目标和需求,然后再进行产品选择。

Pattern Analysis

下表包含事件驱动架构模式的通用架构特征的评级和分析。 对每个特性的评级都基于该特性的自然趋势,即基于模式的典型实现以及该模式通常所知的能力。 有关此模式与本报告中其他模式的关系的并行比较,请参阅本报告末尾的附录A。

Overall agility

评分:高
分析:整体敏捷性是对不断变化的环境做出快速响应的能力。 因为可以快速启动和关闭处理单元(应用程序的部署实例),所以应用程序对与用户负载增加或减少有关的更改(环境更改)反应良好。
使用这种模式创建的体系结构通常会因小应用程序大小和模式的动态性质而对编码更改做出很好的响应。

Ease of deployment

评分:高
分析:尽管基于空间的体系结构通常不分离和分布,但它们是动态的,并且基于云的复杂工具允许将应用程序轻松“推送”到服务器,从而简化了部署。

Testability

评分:低
分析:在测试环境中实现非常高的用户负载既昂贵又耗时,从而难以测试应用程序的可伸缩性方面。

Performance

评分:高
分析:通过内存中的数据访问和此模式中内置的缓存机制可实现高性能。

Scalability

评分:高
分析:高可伸缩性来自于对集中式数据库几乎没有依赖性的事实,因此从本质上消除了可伸缩性方程式中的这一限制瓶颈。

Ease of development

评分:低
分析:复杂的缓存和内存中的数据网格产品使这种模式的开发相对复杂,这主要是由于对创建这种类型的架构所用的工具和产品缺乏了解。 此外,在开发这些类型的体系结构时必须格外小心,以确保源代码中的任何内容都不会影响性能和可伸缩性。

文章目录
  1. 1. Layered Architecture
    1. 1.1. Pattern Description
    2. 1.2. Key Concepts
    3. 1.3. Pattern Example
    4. 1.4. Considerations
    5. 1.5. Pattern Analysis
      1. 1.5.1. Overall agility
      2. 1.5.2. Ease of deployment
      3. 1.5.3. Testability
      4. 1.5.4. Performance
      5. 1.5.5. Scalability
      6. 1.5.6. Ease of development
  2. 2. Event-Driven-Architecture
    1. 2.1. Mediator Topology
    2. 2.2. Broker Topology
    3. 2.3. Considerations
    4. 2.4. Pattern Analysis
      1. 2.4.1. Overall agility
    5. 2.5. Ease of deployment
      1. 2.5.1. Testability
      2. 2.5.2. Performance
      3. 2.5.3. Scalability
      4. 2.5.4. Ease of development
  3. 3. Microkernel Architecture
    1. 3.1. Pattern Description
    2. 3.2. Pattern Examples
    3. 3.3. Considerations
    4. 3.4. Pattern Analysis
      1. 3.4.1. Overall agility
      2. 3.4.2. Ease of deployment
      3. 3.4.3. Testability
      4. 3.4.4. Performance
      5. 3.4.5. Scalability
      6. 3.4.6. Ease of development
  4. 4. Microservices Architecture Pattern
    1. 4.1. Pattern Description
    2. 4.2. Pattern Topologies
    3. 4.3. Avoid Dependencies and Orchestration
    4. 4.4. Considerations
    5. 4.5. Pattern Analysis
      1. 4.5.1. Overall agility
      2. 4.5.2. Ease of deployment
      3. 4.5.3. Testability
      4. 4.5.4. Performance
      5. 4.5.5. Scalability
      6. 4.5.6. Ease of development
  5. 5. Space-Based Architecture
    1. 5.1. 模式说明
    2. 5.2. Pattern Dynamics
      1. 5.2.1. Messaging Grid
      2. 5.2.2. Data Grid
      3. 5.2.3. Processing Grid
    3. 5.3. Deployment Manager
    4. 5.4. Considerations
    5. 5.5. Pattern Analysis
      1. 5.5.1. Overall agility
      2. 5.5.2. Ease of deployment
      3. 5.5.3. Testability
      4. 5.5.4. Performance
      5. 5.5.5. Scalability
      6. 5.5.6. Ease of development
|