Skip to content

samz406/Software-Architecture-Patterns

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 

Repository files navigation

Software-Architecture-Patterns 学习笔记

第1章:分层架构

image

模式描述

分层架构模式是最常见的架构模式之一,也被称为n层架构模式。它是大多数Java EE应用程序的标准,广泛为大多数架构师、设计师和开发人员所熟知。分层架构模式与大多数公司中传统的IT通信和组织结构非常匹配,因此成为大多数业务应用程序开发的自然选择。

在分层架构模式中,组件被组织成水平层,每层在应用程序中扮演特定角色(例如,表示逻辑或业务逻辑)。虽然分层架构模式没有指定必须存在的层数和类型,但大多数分层架构由四个标准层组成:表示层、业务层、持久层和数据库层。在某些情况下,业务层和持久层可能会合并成单一的业务层,尤其是当持久逻辑(例如SQL或HSQL)嵌入在业务层组件中时。因此,小型应用程序可能只有三层,而大型和更复杂的业务应用程序可能包含五层或更多层 。

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

分层架构模式的一个强大功能是组件之间的关注点分离。特定层内的组件只处理与该层相关的逻辑。例如,表示层的组件只处理表示逻辑,而业务层的组件只处理业务逻辑。这种组件分类使得在架构中构建有效的角色和责任模型变得容易,也使得使用这种架构模式开发、测试、管理和维护应用程序变得容易,因为组件接口定义明确且组件范围有限 。

关键概念

在分层架构中,每层都标记为“封闭层”。封闭层意味着请求在层与层之间移动时,必须通过它下方的层才能到达更下方的层。例如,来自表示层的请求必须首先通过业务层,然后再到持久层,最后才到达数据库层 。

分层隔离的概念意味着在架构中一层的更改通常不会影响其他层的组件:更改仅限于该层内的组件,可能还有一个相关的层(例如包含SQL的持久层)。如果允许表示层直接访问持久层,那么对SQL进行的更改将影响业务层和表示层,从而产生一个高度耦合且组件之间具有大量相互依赖的应用程序。这种类型的架构变得非常难以更改且成本高 。

分层隔离的概念还意味着每层是独立于其他层的,对其他层的内部工作知之甚少。考虑到这种概念的力量和重要性,假设将表示框架从JSP(Java Server Pages)转换为JSF(Java Server Faces),只要表示层和业务层之间使用的契约(例如模型)保持不变,业务层不会受到重构的影响,完全独立于表示层使用的用户界面框架类型 。

模式示例

为了说明分层架构如何工作,考虑从业务用户那里获取特定个人的客户信息的请求。请求从表示层传递到数据库以获取客户数据,响应则返回并显示在屏幕上。客户信息包括客户数据和订单数据(客户下的订单)。客户屏幕负责接受请求并显示客户信息。它不知道数据的位置、如何获取数据或必须查询多少数据库表才能获取数据 。

客户屏幕收到请求后,将其转发给客户委派模块。该模块负责知道业务层中哪个模块可以处理请求以及如何到达该模块以及需要什么数据。业务层中的客户对象负责汇总业务请求所需的所有信息。该模块调用持久层中的客户数据访问对象模块以获取客户数据,并调用订单数据访问对象模块以获取订单信息。这些模块依次执行SQL语句以检索相应的数据,并将其传递回业务层的客户对象。一旦客户对象收到数据,它会汇总数据并将信息传递回客户委派,后者再将数据传递给客户屏幕以显示给用户 。

从技术角度来看,可以通过多种方式实现这些模块。例如,在Java平台中,客户屏幕可以是JSF(Java Server Faces)屏幕,客户委派可以是托管Bean组件。业务层中的客户对象可以是本地Spring Bean或远程EJB3 Bean。持久层中的数据访问对象可以实现为简单的POJO(Plain Old Java Objects)、MyBatis XML映射文件,甚至是封装了原始JDBC调用或Hibernate查询的对象。在Microsoft平台上,客户屏幕可以是ASP(Active Server Pages)模块,使用.NET框架访问业务层中的C#模块,客户和订单数据访问模块实现为ADO(ActiveX Data Objects) 。

###考虑事项 分层架构模式是一个通用的模式,使其成为大多数应用程序的良好起点,尤其是当你不确定哪种架构模式最适合你的应用程序时。然而,从架构角度来看,选择此模式时需要考虑一些事项。

首先要注意的是“架构陷阱”反模式。该反模式描述了请求通过架构的多层流动,作为简单的传递处理,而每层内几乎没有逻辑。例如,假设表示层响应用户请求以检索客户数据。表示层将请求传递到业务层,业务层再将请求传递到持久层,后者通过简单的SQL调用数据库层以检索客户数据。数据随后一路传递回表示层,而没有任何额外的处理或逻辑来汇总、计算或转换数据 。

每个分层架构都会有一些场景落入架构陷阱反模式。关键是要分析落入该类别的请求百分比。通常的做法是遵循80-20规则,即大约20%的请求是简单的传递处理,80%的请求具有与请求相关的某些业务逻辑。然而,如果发现这个比例颠倒了,大多数请求都是简单的传递处理,那么你可能需要考虑将一些架构层开放,但要注意这会使控制更改变得更难,因为缺乏层隔离 。

另一个需要考虑的是,分层架构模式往往倾向于单体应用程序,即使将表示层和业务层拆分为单独的可部署单元。这对某些应用程序来说可能不是问题,但在部署、总体稳健性和可靠性、性能和可扩展性方面可能会带来潜在问题 。

模式分析

下面的表格包含了对分层架构模式常见架构特征的评分和分析。每个特征的评分基于该特征在典型实现中的自然倾向以及该模式通常所知的能力。

架构特征 评分 分析
整体敏捷性 整体敏捷性是指快速响应不断变化的环境的能力。虽然通过层隔离功能可以隔离更改,但由于大多数实现的单体性质以及通常与该模式相关的组件紧耦合,做出更改仍然是繁琐且耗时的。
部署便捷性 根据你的实现方式,部署可能成为一个问题,特别是对于较大的应用程序。对组件进行一次小改动可能需要重新部署整个应用程序(或大部分应用程序),导致部署需要计划、安排并在非工作时间或周末执行。因此,该模式不容易适应连续交付管道,从而进一步降低了整体部署评分。
可测试性 由于组件属于架构中的特定层,其他层可以被模拟或存根,使得这个模式相对容易测试。开发人员可以模拟表示组件或屏幕以隔离业务组件内的测试,也可以模拟业务层以测试某些屏幕功能。
性能 虽然某些分层架构可以表现良好,但由于必须通过架构的多个层来满足业务请求,这种模式并不倾向于高性能应用程序。
可扩展性 由于该模式往往与紧耦合和单体实现相关,使用该模式难以实现高扩展性。

第二章:事件驱动架构

image

模式描述

事件驱动架构(Event-Driven Architecture,EDA)是一种流行的分布式异步架构模式,用于创建高度可扩展的应用程序。EDA的基本理念是通过事件来驱动系统中的变化,这些事件在系统中异步传播,从而实现松耦合和高并发处理。EDA被广泛应用于金融服务、电子商务、物联网等领域,能够有效处理高吞吐量和复杂业务逻辑。

事件驱动架构模式主要包括两种拓扑结构:中介者拓扑(Mediator Topology)和代理拓扑(Broker Topology)。中介者拓扑通常用于需要通过中央中介者协调多个步骤的事件处理,而代理拓扑则用于希望将事件链式连接起来而无需中央中介者的场景。理解这两种拓扑结构的适用场景和实现策略对于成功应用EDA至关重要。

中介者拓扑

中介者拓扑适用于包含多个步骤且需要某种级别协调来处理事件的情况。例如,单个股票交易事件可能需要首先验证交易,然后检查交易的合规性,分配交易给经纪人,计算佣金,最后将交易提交给经纪人。所有这些步骤都需要一定的协调,以确定步骤的顺序以及哪些步骤可以串行或并行执行。

在中介者拓扑中,有四种主要的架构组件:事件队列(Event Queue)、事件中介者(Event Mediator)、事件通道(Event Channel)和事件处理器(Event Processor)。事件流从客户端发送事件到事件队列开始,事件队列用于将事件传输到事件中介者。事件中介者接收初始事件,并通过发送额外的异步事件到事件通道来协调该事件的每一步骤。监听事件通道的事件处理器接收来自事件中介者的事件,并执行特定的业务逻辑以处理事件。

事件队列:事件队列是中介者拓扑的入口,负责接收和存储来自客户端的初始事件。事件队列的实现可以基于消息队列技术,如RabbitMQ、Apache Kafka等。事件队列的主要作用是解耦事件生产者和事件消费者,提供事件的可靠传输和持久化存储。

事件中介者:事件中介者是中介者拓扑的核心组件,负责协调和管理事件的处理流程。事件中介者接收事件队列中的初始事件,并根据预定义的业务规则将其分解为多个处理步骤。每个步骤生成一个新的事件,这些事件通过事件通道传递给相应的事件处理器。事件中介者的实现可以基于业务流程管理(BPM)工具,如Camunda、Activiti等,也可以通过自定义代码实现。

事件通道:事件通道用于传输由事件中介者生成的处理事件。事件通道可以是消息队列或消息主题,取决于事件处理的具体需求。消息队列适用于点对点的消息传递,而消息主题则适用于发布/订阅模式,允许多个事件处理器同时接收同一个事件。

事件处理器:事件处理器是执行具体业务逻辑的组件,每个事件处理器负责处理一种特定类型的事件。事件处理器可以是独立的微服务、函数或应用程序内的模块。事件处理器接收到事件后,执行相应的业务逻辑,并根据需要生成新的事件或更新系统状态。事件处理器的实现可以基于多种技术,如Spring Boot微服务、AWS Lambda函数等。

中介者拓扑的优点是能够对复杂的业务流程进行集中管理和协调,确保业务流程的正确执行和事件处理的顺序性。然而,其缺点是事件中介者可能成为系统的性能瓶颈和单点故障,需要对事件中介者进行高可用性和负载均衡设计。

代理拓扑

代理拓扑与中介者拓扑不同,代理拓扑没有中央事件中介者;相反,消息流通过轻量级消息代理(如ActiveMQ、HornetQ等)在事件处理器组件中以链式方式分布。这种拓扑结构适用于具有相对简单的事件处理流,并且不需要中央事件协调的情况。

代理拓扑中的架构组件主要包括代理组件(Broker Component)和事件处理器组件。代理组件可以是集中式的或联合式的,包含事件流中使用的所有事件通道。代理组件中的事件通道可以是消息队列、消息主题或两者的组合。

代理组件:代理组件是代理拓扑的核心,负责管理事件的传输和分发。代理组件通过消息队列或消息主题将事件从一个事件处理器传递到下一个事件处理器。代理组件可以实现消息的持久化存储、负载均衡和故障恢复,确保事件的可靠传输和处理。

事件处理器组件:事件处理器组件是执行具体业务逻辑的模块,每个事件处理器组件负责处理一种特定类型的事件。事件处理器组件接收到事件后,执行相应的业务逻辑,并根据需要生成新的事件或更新系统状态。事件处理器组件之间通过代理组件进行通信,实现事件的链式处理。

代理拓扑的优点是结构简单、实现灵活,适用于无需复杂协调的事件处理场景。其缺点是事件处理流程分散在多个事件处理器组件中,缺乏集中管理和协调,可能导致业务流程的可视性和可控性降低。

实施示例

为了说明中介者拓扑的工作方式,假设一个保险公司用户搬家。初始事件可能称为搬迁事件(Relocation Event)。事件中介者会创建处理事件(如变更地址、重新计算报价等),并将这些事件发送到事件通道,等待相应的事件处理器(如客户处理器、报价处理器)处理。这个过程会持续进行,直到所有步骤都处理完毕。

在代理拓扑中,没有中央事件中介者接收初始事件,客户处理组件直接接收事件,变更客户地址,并发送一个变更地址事件。在这个示例中,有两个事件处理器对变更地址事件感兴趣:报价处理器和索赔处理器。报价处理器重新计算新的汽车保险费率,并发布一个重新计算报价事件。索赔处理器更新未决的保险索赔,并发布一个更新索赔事件。这些新事件随后由其他事件处理器组件接收,事件链在系统中继续,直到不再发布更多事件为止。

考虑事项 事件驱动架构模式相对复杂,主要由于其异步分布式特性。在实施这种模式时,必须解决各种分布式架构问题,如远程进程可用性、响应不足以及在代理或中介者故障时的重新连接逻辑。

选择这种架构模式时,需要考虑一个问题是缺乏单一业务流程的原子事务。由于事件处理器组件高度解耦且分布式,维护跨组件的事务单元工作非常困难。因此,在设计使用这种模式的应用程序时,必须不断思考哪些事件可以独立运行,哪些不能,并相应地计划事件处理器的粒度。如果发现需要跨事件处理器拆分单一工作单元,这可能不是适合您应用程序的正确模式。

事件驱动架构模式最难的方面之一是事件处理器组件合同的创建、维护和管理。每个事件通常都有一个特定的合同(例如,传递给事件处理器的数据值和数据格式)。使用这种模式时,至关重要的是从一开始就确定标准的数据格式(例如XML、JSON、Java对象)并建立合同版本控制策略。

模式分析

架构特征 评分 分析
整体敏捷性 由于事件处理器组件是单一目的并且完全解耦,变化通常仅限于一个或少数几个事件处理器,可以快速进行更改而不影响其他组件。
部署便捷性 由于事件处理器组件的解耦性质,该模式相对容易部署。代理拓扑比中介者拓扑更容易部署,主要因为事件中介者组件与事件处理器组件紧密耦合。
可测试性 虽然单元测试并不困难,但需要某种专门的测试客户端或工具来生成事件。由于该模式的异步性质,测试也变得复杂。
性能 尽管由于消息基础设施的开销,可能会影响性能,但一般来说,该模式通过其异步能力实现高性能。
可扩展性 由于事件处理器组件高度解耦,这种模式通常具有良好的可扩展性。可以根据需求轻松添加或移除事件处理器组件,从而支持大规模的扩展。

第三章:微内核架构

image

模式描述

微内核架构模式(有时称为插件架构模式)是一种自然适用于实现基于产品的应用程序的模式。基于产品的应用程序是指打包并以版本形式提供下载的典型第三方产品。然而,许多公司也开发和发布其内部业务应用程序,像软件产品一样,完整的版本、发布说明和可插拔功能。这些也是这种模式的自然适用对象。微内核架构模式允许您将其他应用程序功能作为插件添加到核心应用程序中,从而提供可扩展性以及功能分离和隔离。

模式描述

微内核架构模式由两种类型的架构组件组成:核心系统和插件模块。应用程序逻辑在独立的插件模块和基本核心系统之间划分,提供扩展性、灵活性和应用功能与自定义处理逻辑的隔离。下图展示了基本的微内核架构模式。

微内核架构模式的核心系统传统上只包含使系统运行所需的最小功能。许多操作系统实现了微内核架构模式,因此这种模式的名称由此而来。从业务应用程序的角度来看,核心系统通常被定义为一般业务逻辑,不包括特殊情况的自定义代码、特殊规则或复杂的条件处理。

插件模块是独立的、独立的组件,包含专门的处理、附加功能和自定义代码,旨在增强或扩展核心系统,以提供额外的业务能力。通常,插件模块应独立于其他插件模块,但您当然可以设计需要其他插件存在的插件。无论哪种情况,尽量保持插件之间的通信最少,以避免依赖问题。

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

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

插件模块与核心系统之间的契约可以是标准契约,也可以是自定义契约。自定义契约通常出现在插件组件由第三方开发而您无法控制插件使用的契约的情况下。在这种情况下,通常会在插件契约和您的标准契约之间创建一个适配器,以便核心系统不需要为每个插件编写专门的代码。当创建标准契约时(通常通过XML或Java Map实现),从一开始就创建版本控制策略非常重要。

模式示例

也许微内核架构最好的例子是Eclipse IDE。下载基本的Eclipse产品后,您几乎只是一个花哨的编辑器。然而,一旦开始添加插件,它就变成了一个高度可定制和有用的产品。互联网浏览器是使用微内核架构的另一个常见产品示例:查看器和其他插件增加了基本浏览器中未发现的附加功能。

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

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

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

下图展示了索赔处理的微内核架构示例:

图中的文件夹堆代表索赔处理的核心系统。它包含保险公司处理索赔所需的基本业务逻辑,但不包括任何自定义处理。每个插件模块包含特定州的规则。在这个示例中,插件模块可以使用自定义源代码或单独的规则引擎实例实现。无论实现如何,关键点是特定州的规则和处理与核心索赔系统分开,可以添加、删除和更改,而对核心系统或其他插件模块的影响很小或没有影响。

考虑事项

微内核架构模式的一个优点是它可以嵌入或作为另一种架构模式的一部分使用。例如,如果这种模式解决了您在应用程序中某个特定易变区域的问题,您可能会发现无法使用这种模式实现整个架构。在这种情况下,您可以将微内核架构模式嵌入到您使用的另一种模式中(例如,分层架构)。类似地,可以使用微内核架构模式实现上一节中描述的事件驱动架构的事件处理器组件。

微内核架构模式为进化设计和增量开发提供了很好的支持。您可以首先生成一个稳定的核心系统,并随着应用程序的演进逐步添加功能和功能,而无需对核心系统进行重大更改。

对于基于产品的应用程序,微内核架构模式应该始终是您的首选起始架构,特别是对于那些您将随着时间发布附加功能并希望控制哪些用户获得哪些功能的产品。如果您随着时间的推移发现这种模式无法满足所有要求,您可以始终将应用程序重构为更适合您特定要求的另一种架构模式。

模式分析

架构特征 评分 分析
整体敏捷性 整体敏捷性是快速响应不断变化的环境的能力。由于可以通过松散耦合的插件模块隔离和快速实现更改,因此总体上,使用这种架构的应用程序 tend to become stable quickly and require few changes over time.
部署便捷性 根据实现方式,插件模块可以在运行时动态添加到核心系统中(例如,热部署),从而在部署期间将停机时间降至最低。
可测试性 插件模块可以在隔离状态下进行测试,并且可以轻松地由核心系统模拟,以展示或原型化特定功能,而对核心系统的更改很少或没有。
性能 虽然微内核模式并不自然地适合高性能应用程序,但总体上,大多数使用微内核架构模式构建的应用程序表现良好,因为您可以自定义和精简应用程序以仅包含所需的功能。JBoss应用服务器就是一个很好的例子:通过其插件架构,您可以修剪应用服务器以仅包含所需的功能,从而移除占用内存、CPU和线程并减慢应用服务器的昂贵非使用功能,如远程访问、消息传递和缓存。
可扩展性 由于大多数微内核架构实现都是基于产品的,通常较小,因此作为单一单元实施,并且不具有高度可扩展性。根据插件模块的实现方式,您有时可以在插件功能级别提供可扩展性,但总体上该模式并不以生成高度可扩展的应用程序而闻名。
开发便捷性 微内核架构需要深思熟虑的设计和契约治理,使其实现相对复杂。契约版本控制、内部插件注册表、插件粒度以及可用于插件连接的多种选择都增加了实现此模式的复杂性。

第四章:微服务架构模式

image

模式描述

微服务架构模式迅速在业界获得认可,作为单体应用和面向服务架构(SOA)的可行替代方案。由于这种架构模式仍在发展,业界对其定义和实现存在许多混淆。本章将为您提供理解这一重要架构模式的关键概念和基础知识,帮助您了解其优势(和权衡),并判断其是否适合您的应用程序。

无论您选择何种拓扑或实现风格,微服务架构模式都具有一些共同的核心概念。第一个概念是独立部署单元的概念。如图4-1所示,微服务架构的每个组件都作为一个独立单元部署,从而通过有效的交付管道提高部署效率和灵活性,增加可扩展性,并在应用程序内实现高度解耦。

或许理解这一模式最重要的概念是服务组件的概念。与其在微服务架构中思考服务,不如将其视为服务组件,这些组件的粒度可以从单一模块到应用程序的大部分不等。服务组件包含一个或多个模块(例如Java类),代表单一功能(例如提供特定城市或城镇的天气)或大型业务应用程序的独立部分(例如股票交易下单或确定汽车保险费率)。设计服务组件的正确粒度是微服务架构中的最大挑战之一。

模式拓扑

虽然有许多方式可以实现微服务架构模式,但有三种主要拓扑最为常见和流行:基于API的REST拓扑、基于应用的REST拓扑和集中消息传递拓扑。

基于API的REST拓扑 适用于通过某种API(应用程序编程接口)暴露小型、自包含服务的网站。这种拓扑包含非常细粒度的服务组件(因此称为微服务),这些组件包含一个或两个模块,执行独立于其他服务的特定业务功能。通常,这些细粒度的服务组件通过REST接口访问,该接口通过单独部署的基于Web的API层实现。

基于应用的REST拓扑 与基于API的REST方法不同,客户端请求通过传统的基于Web的或胖客户端的业务应用程序屏幕接收,而不是通过简单的API层。这种拓扑中的用户界面层作为单独的Web应用程序部署,通过简单的REST接口远程访问独立部署的服务组件。这些服务组件的粒度通常较粗,代表整个业务应用程序的一小部分,而不是细粒度的单一功能服务。

集中消息传递拓扑 是微服务架构中常见的另一种方法。与前面的基于应用的REST拓扑类似,不同之处在于这种拓扑使用轻量级集中消息代理(例如ActiveMQ、HornetQ等)而不是REST进行远程访问。这种拓扑通常出现在较大的业务应用程序或需要更复杂的传输层控制的应用程序中。通过代理集群和代理联合(将单个代理实例分成多个代理实例,根据系统的功能区域划分消息吞吐量),解决了集中代理通常面临的单点故障和架构瓶颈问题。

避免依赖和编排 微服务架构模式的一个主要挑战是确定服务组件的正确粒度。如果服务组件过于粗粒,可能无法实现此架构模式带来的优势(部署、可扩展性、可测试性和松散耦合)。然而,过于细粒度的服务组件会导致服务编排需求,迅速将精简的微服务架构转变为重量级的面向服务架构,伴随复杂性、混乱、费用和常见于基于SOA的应用程序的冗余。

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

服务组件之间的内部通信会强制产生不必要的耦合,可以通过共享数据库来处理。例如,如果处理互联网订单的服务组件需要客户信息,它可以访问数据库获取必要的数据,而不是调用客户服务组件内的功能。

共享数据库可以处理信息需求,但共享功能呢?如果服务组件需要其他服务组件中包含的功能或所有服务组件共有的功能,可以将共享功能复制到各个服务组件中(从而违反了DRY原则:不要重复自己)。这是大多数实现微服务架构模式的业务应用程序中的一种常见做法,牺牲小部分业务逻辑的冗余,以保持服务组件的独立性和部署的分离。小型工具类可能属于这种重复代码的范畴。

如果发现无论服务组件的粒度如何,仍无法避免服务组件的编排,那么这是一个迹象,表明这可能不是适合您应用程序的正确架构模式。由于这种模式的分布式性质,很难在服务组件之间维护单一的事务工作单元。这种做法需要某种事务补偿框架来回滚事务,这为这种相对简单和优雅的架构模式增加了显著的复杂性。

考虑事项

微服务架构模式解决了单体应用和面向服务架构中常见的许多问题。由于主要应用程序组件分割成较小的、独立部署的单元,使用微服务架构模式构建的应用程序通常更加稳健,提供更好的可扩展性,并且更容易支持持续交付。

这种模式的另一个优势是可以进行实时生产部署,从而显著减少传统的月度或周末“大爆炸”式生产部署的需求。由于更改通常仅限于特定的服务组件,因此只需要部署更改的服务组件。如果只有一个服务组件实例,可以在用户界面应用程序中编写专门的代码来检测活动的热部署,并将用户重定向到错误页面或等待页面。或者,可以在实时部署期间交换服务组件的多个实例,从而在部署周期中保持连续可用性(这种做法在分层架构模式中非常困难)。

最后需要考虑的是,由于微服务架构模式是分布式架构,它与事件驱动架构模式共享一些复杂问题,包括契约创建、维护和治理、远程系统可用性以及远程访问认证和授权。

模式分析

架构特征 评分 分析
整体敏捷性 整体敏捷性是快速响应不断变化的环境的能力。由于独立部署单元的概念,更改通常限于单个服务组件,从而实现快速和轻松的部署。此外,使用此模式构建的应用程序通常高度松散耦合,这也有助于促进更改。
部署便捷性 由于远程服务的细粒度和独立性,微服务模式的部署特性得分很高。服务通常作为独立的软件单元部署,从而能够在任何时间进行“热部署”。总体部署风险也显著降低,因为失败的部署可以更快恢复,并且只影响正在部署的服务操作,从而保证其他操作的连续运行。
可测试性 由于业务功能分离为独立应用程序,测试范围可以缩小,从而更有针对性地进行测试。特定服务组件的回归测试比整个单体应用程序的回归测试更容易和更可行。此外,由于这种模式中的服务组件松散耦合,从开发角度来看,更改某一部分代码导致应用程序其他部分出错的几率大大降低,从而减轻了测试负担。
性能 虽然可以使用这种模式构建性能非常好的应用程序,但总体上,这种模式并不自然地适合高性能应用程序,因为微服务架构模式的分布式性质。
可扩展性 由于应用程序分为独立部署单元,可以单独扩展每个服务组件,从而实现应用程序的精细调优。例如,股票交易应用程序的管理区域可能不需要扩展,因为该功能的用户量较低,但交易下单服务组件可能需要扩展,因为大多数交易应用程序的这一功能需要高吞吐量。
开发便捷性 由于功能分离为独立和独特的服务组件,开发变得更容易,因为范围更小且独立。开发人员在一个服务组件中进行更改而影响其他服务组件的几率大大降低,从而减少了开发人员或开发团队之间的协调需求。

第五章:基于空间的架构

image

模式描述

基于空间的架构模式(有时称为云架构模式)旨在最大限度地减少限制应用程序扩展的因素。该模式的名字来源于“元组空间”(tuple space)的概念,即分布式共享内存的理念。通过消除中央数据库的限制并使用复制的内存数据网格,可以实现高扩展性。应用程序数据保存在内存中,并在所有活动处理单元之间复制。当用户负载增加和减少时,可以动态启动和关闭处理单元,从而解决可变的可扩展性问题。由于没有中央数据库,数据库瓶颈被消除,从而在应用程序中提供了几乎无限的可扩展性。

适合此模式的大多数应用程序是标准网站,它们接收来自浏览器的请求并执行某种操作。一个竞价拍卖网站就是一个很好的例子。该网站通过浏览器请求不断接收用户的出价。应用程序会接收某个物品的出价,记录该出价及其时间戳,更新物品的最新出价信息,并将信息发送回浏览器。

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

虚拟化中间件组件处理后台工作和通信。它包含控制数据同步和请求处理的各个方面的组件。包括消息网格、数据网格、处理网格和部署管理器。这些组件可以是定制编写的,也可以是购买的第三方产品。

模式动态

基于空间的架构模式的神奇之处在于虚拟化中间件组件和每个处理单元中包含的内存数据网格。虚拟化中间件本质上是架构的控制器,管理请求、会话、数据复制、分布式请求处理和处理单元的部署。虚拟化中间件中的四个主要架构组件如下:

消息网格:消息网格管理输入请求和会话信息。当请求进入虚拟化中间件组件时,消息网格组件确定哪些活动处理组件可用以接收请求,并将请求转发给其中一个处理单元。消息网格的复杂性可以从简单的轮询算法到更复杂的下一个可用算法,该算法跟踪哪个请求正在由哪个处理单元处理。

数据网格:数据网格组件可能是该模式中最重要和关键的组件。数据网格与每个处理单元中的数据复制引擎交互,以在数据更新时管理处理单元之间的数据复制。由于消息网格可以将请求转发给任何可用的处理单元,因此每个处理单元中必须包含完全相同的数据。虽然图示中显示了处理单元之间的同步数据复制,但实际上这是并行异步完成的,有时数据同步在微秒(百万分之一秒)内完成。

处理网格:处理网格是虚拟化中间件中的可选组件,当有多个处理单元处理应用程序的一部分时,处理网格管理分布式请求处理。如果请求需要处理单元类型之间的协调(例如订单处理单元和客户处理单元),处理网格在这些处理单元之间进行调解和编排。

部署管理器:部署管理器组件根据负载条件管理处理单元的动态启动和关闭。该组件持续监控响应时间和用户负载,当负载增加时启动新处理单元,当负载减少时关闭处理单元。部署管理器是实现应用程序可变扩展需求的关键组件。

考虑事项

基于空间的架构模式是一种复杂且昂贵的实现模式。它是具有可变负载的小型Web应用程序(例如社交媒体网站、竞价和拍卖网站)的良好架构选择。然而,它并不适合具有大量操作数据的传统大型关系数据库应用程序。

虽然基于空间的架构模式不需要集中式数据存储,但通常包括一个用于执行初始内存数据网格加载并异步持久化处理单元所做的数据更新。为了减少每个处理单元内存数据网格的内存占用,通常会创建独立的分区以隔离易失性和广泛使用的事务数据与非活动数据。

虽然该模式的替代名称是云架构,但处理单元(以及虚拟化中间件)不必驻留在基于云的托管服务或PaaS(平台即服务)上。它们同样可以驻留在本地服务器上,这是我更喜欢“基于空间的架构”这个名称的原因之一。

从产品实现的角度来看,可以通过第三方产品实现该模式中的许多架构组件,例如GemFire、JavaSpaces、GigaSpaces、IBM Object Grid、nCache和Oracle Coherence。由于该模式的实现方式在成本和功能方面差异很大(特别是数据复制时间),作为架构师,您应首先确定您的具体目标和需求,然后再做出产品选择。

模式分析

架构特征 评分 分析
整体敏捷性 整体敏捷性是快速响应不断变化的环境的能力。由于可以快速启动和关闭处理单元,应用程序对用户负载的增加或减少响应良好。通过使用此模式创建的架构通常对代码更改反应良好,因为应用程序规模小且模式具有动态性。
部署便捷性 虽然基于空间的架构通常不是解耦和分布式的,但它们是动态的,复杂的云工具允许应用程序轻松“推送”到服务器,简化了部署。
可测试性 在测试环境中实现非常高的用户负载既昂贵又费时,使得测试应用程序的可扩展性变得困难。
性能 通过模式中内置的内存数据访问和缓存机制实现高性能。
可扩展性 高可扩展性源于对集中式数据库几乎没有或没有依赖,从而从可扩展性方程中基本消除了这一限制性瓶颈。
开发便捷性 高级缓存和内存数据网格产品使得开发这种模式相对复杂,主要是因为缺乏对使用的工具和产品的熟悉。此外,在开发这些类型的架构时必须特别小心,以确保源代码中的任何内容不会影响性能和可扩展性。

About

Software Architecture Patterns

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published