架构演进

架构演进史

软件架构是有关软件整体结构与组件的抽象描述,用于指导大型软件系统各个方面的设计。软件架构会包括软件组件、组件之间的关系,组件特性以及组件间关系的特性。软件架构可以和建筑物的架构相比拟。软件架构是构建电脑软件,开发系统以及计划进行的基础,可以列出开发团队需要完成的任务。

架构是软件持续演进的产物,从历史演进来看,目前架构总体演进过程分为

  • 原始分布式时代
  • 单体系统时代
  • SOA 时代
  • 微服务时代
  • 云原生时代
  • 无服务时代

架构模式

架构模式(Architectural Pattern)是软件架构中在给定环境下,针对常遇到的问题的、通用且可重用的解决方案。类似于软件设计模式但覆盖范围更广,致力于软件工程中不同问题,如计算机硬件性能限制、高可用性、业务风险极小化。一些架构模式会透过软件框架实现。

分层架构模式

分层架构模式(Layered Architecture Pattern)是架构模式中最常见的架构模式之一,也称为 n 层架构模式,这种架构模式大多数应用于 Java EE 应用程序中,且为大多数架构师,设计师和开发人员所熟悉,分层架构模式与大多数公司的传统 IT 通信和组织结构非常匹配,使其成为大多数业务应用程序开发工作的自然选择。

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

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

《分层架构模式》

分层架构特征:

  • 整体敏捷性低:整体敏捷性是对不断变化的环境做出快速反应的能力。虽然可以通过此模式的隔离功能层来隔离更改,但由于大多数实现的单体性质以及通常与此相关的组件的紧密耦合,因此在此架构模式中进行更改仍然很麻烦且耗时图案。
  • 部署成本低:正因如此,此模式部署可能成为一个问题,尤其是对于大型应用程序。对组件的一个小更改可能需要重新部署整个应用程序(或应用程序的大部分),导致需要在下班时间或周末计划、安排和执行部署。因此,这种模式不容易适应持续交付管道,进一步降低了部署的整体评级。
  • 可测试性高:由于组件属于架构中的特定层,其他层可以被模拟或存根,使得这种模式相对容易测试。开发人员可以模拟表示组件或屏幕以隔离业务组件内的测试,也可以模拟业务层以测试某些屏幕功能。
  • 性能低:虽然某些分层架构确实可以很好地执行,但由于必须通过架构的多层来满足业务请求效率低下,因此该模式不适合高性能应用程序。
  • 扩展性低:由于这种模式的紧密耦合和整体实现的趋势,使用这种架构模式构建的应用程序通常难以扩展。您可以通过将层拆分为单独的物理部署或将整个应用程序复制到多个节点来扩展分层架构,但总体而言,粒度太宽,导致扩展成本高昂。
  • 开发成本低:分层架构比较明显的就是易于开发,主要是因为这种模式众所周知并且实现起来并不复杂。由于大多数公司通过按层(表示、业务、数据库)分离技能集来开发应用程序,因此这种模式成为大多数业务应用程序开发的自然选择。公司的沟通和组织结构与其开发软件的方式之间的联系被概述为所谓的康威定律。您可以谷歌“康威定律”以获取有关这种迷人相关性的更多信息。

事件驱动架构模式

事件驱动架构模式(Event-Driven Architecture Pattern)是一种流行的分布式异步架构模式,用于生成高度可扩展的应用程序。它还具有很强的适应性,既可用于小型应用程序,也可用于大型、复杂的应用程序。事件驱动架构由高度分离的、单一用途的事件处理组件组成,这些组件异步接收和处理事件。

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

《事件驱动架构模式》

在事件驱动的架构中,通常有十几个到几百个事件队列。该模式没有指定事件队列组件的实现;它可以是消息队列、Web 服务端点或它们的任意组合。

事件驱动架构特征:

  • 整体敏捷性高:整体敏捷性是对不断变化的环境做出快速反应的能力。由于事件处理器组件是单一用途的,并且与其他事件处理器组件完全分离,因此更改通常隔离到一个或几个事件处理器,并且可以在不影响其他组件的情况下快速进行。
  • 部署升本高:总体而言,由于事件处理器组件的解耦特性,这种模式相对容易部署。代理拓扑往往比中介拓扑更容易部署,主要是因为事件中介组件在某种程度上与事件处理器紧密耦合:事件处理器组件的变化可能也需要事件中介的变化,要求两者都部署任何给定的变化。
  • 可测试性低:虽然单个单元测试并不过分困难,但它确实需要某种专门的测试客户端或测试工具来生成事件。这种模式的异步性质也使测试变得复杂。
  • 性能高:虽然由于涉及所有消息传递基础设施,实现一个性能不佳的事件驱动架构当然是可能的,但一般来说,该模式通过其异步功能实现高性能;换句话说,执行解耦的并行异步操作的能力超过了排队和出队消息的成本。
  • 可扩展性高:在这种模式下,可扩展性自然是通过高度独立和解耦的事件处理器来实现的。每个事件处理器都可以单独扩展,从而实现细粒度的可扩展性。
  • 开发成本低:由于模式的异步性质以及合约创建以及代码中对无响应事件处理器和失败代理的更高级错误处理条件的需求,开发可能会有些复杂。

微内核架构模式

微内核架构模式(Microkernel Architectur Pattern)(有时称为插件架构模式)是实现基于产品的应用程序的自然模式。基于产品的应用程序是作为典型的第三方产品打包并提供多种版本供下载的应用程序。但是,许多公司还开发和发布其内部业务应用程序,如软件产品,包括版本、发行说明和可插入功能。这些也很适合这种模式。微内核架构模式允许您将额外的应用程序功能作为插件添加到核心应用程序,提供可扩展性以及功能分离和隔离。

微内核架构模式的核心系统传统上只包含使系统运行所需的最少功能。许多操作系统都实现了微内核架构模式,这也是该模式名称的由来。从业务应用的角度来看,核心系统通常被定义为通用业务逻辑,没有针对特殊情况、特殊规则或复杂条件处理的自定义代码。

《微内核架构模式》

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

微内核架构特征:

  • 整体敏捷性高:整体敏捷性是对不断变化的环境做出快速反应的能力。通过松散耦合的插件模块,可以在很大程度上隔离和快速实施更改。一般来说,大多数微内核架构的核心系统往往会很快变得稳定,因此相当健壮,并且随着时间的推移几乎不需要更改。
  • 部署成本高:根据模式的实现方式,插件模块可以在运行时动态添加到核心系统(例如,热部署),从而最大限度地减少部署期间的停机时间。
  • 可测试性高:插件模块可以单独测试,并且可以很容易地被核心系统模拟,以演示或原型化特定功能,而对核心系统几乎没有变化或没有变化。
  • 性能高:虽然微内核模式本身并不适合高性能应用程序,但一般来说,大多数使用微内核架构模式构建的应用程序都表现良好,因为您可以自定义和简化应用程序以仅包含您需要的那些功能。 JBoss 应用服务器就是一个很好的例子:借助其插件架构,您可以将应用服务器缩减为仅包含您需要的功能,删除昂贵的未使用功能,例如消耗内存的远程访问、消息传递和缓存、CPU 和线程,并降低应用服务器的速度。
  • 可扩展性低:由于大多数微内核架构实现都是基于产品的,并且通常规模较小,因此它们被实现为单个单元,因此可扩展性不高。根据您实现插件模块的方式,您有时可以在插件功能级别提供可伸缩性,但总体而言,这种模式并不以生成高度可伸缩的应用程序而闻名。
  • 开发成本低:微内核架构需要深思熟虑的设计和契约治理,实现起来相当复杂。合同版本控制、内部插件注册表、插件粒度以及可用于插件连接的广泛选择都增加了实现此模式所涉及的复杂性。

微服务架构模式

微服务架构模式(Microservices Architecture Pattern)作为单一应用程序和面向服务架构的可行替代方案,正在行业中迅速普及。由于这种架构模式仍在不断发展,因此业界对这种模式的全部内容及其实现方式存在很多困惑。报告的这一部分将为您提供了解这一重要架构模式的好处(和权衡)以及它是否适合您的应用程序所必需的关键概念和基础知识。

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

《基本微服务架构模式》

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

微服务架构模式特征:

  • 整体敏捷性高:整体敏捷性是对不断变化的环境做出快速反应的能力。由于单独部署单元的概念,更改通常与单个服务组件隔离,从而可以快速轻松地进行部署。此外,使用此模式构建的应用程序往往耦合度非常低,这也有助于促进更改。
  • 部署成本高:由于远程服务的细粒度和独立性,微服务模式的部署率非常高。服务通常作为单独的软件单元部署,因此能够在白天或晚上的任何时间进行“热部署”。总体部署风险也显着降低,因为失败的部署能够更快地恢复并且只影响正在部署的服务的操作,从而导致所有其他操作的继续操作。
  • 可测试性高:由于将业务功能分离和隔离到独立的应用程序中,可以确定测试范围,从而允许更有针对性的测试工作。与针对整个单体应用程序的回归测试相比,针对特定服务组件的回归测试要容易得多,也更可行。此外,由于这种模式中的服务组件是松散耦合的,从开发的角度来看,更改会破坏应用程序的另一部分的可能性要小得多,从而减轻了必须为一个小的部分测试整个应用程序的测试负担改变。
  • 性能低:虽然您可以创建从这种模式实现的应用程序,并且性能非常好,但总体而言,由于微服务架构模式的分布式特性,这种模式本身并不适合高性能应用程序。
  • 可扩展性高:因为应用程序被拆分成单独部署的单元,所以每个服务组件都可以单独扩展,从而允许微调应用程序的扩展。例如,股票交易应用程序的管理区域可能不需要扩展,因为该功能的用户量较低,但交易配售服务组件可能需要扩展,因为大多数交易应用程序为此需要高吞吐量功能。
  • 开发成本高:由于功能被隔离到独立且不同的服务组件中,因此由于范围更小且隔离,开发变得更容易。开发人员更改一个服务组件而影响其他服务组件的可能性要小得多,从而减少开发人员或开发团队之间所需的协调。

基于空间的架构

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

大多数基于 Web 的业务应用程序都遵循相同的一般请求流程:来自浏览器的请求到达 Web 服务器,然后是应用程序服务器,最后是数据库服务器。虽然这种模式适用于一小部分用户,但随着用户负载的增加,瓶颈开始出现,首先是在网络服务器层,然后是应用程序服务器层,最后是数据库服务器层。对基于用户负载增加的瓶颈的通常响应是扩展 Web 服务器。这相对容易且成本低廉,有时可以解决瓶颈问题。然而,在大多数高用户负载的情况下,扩展 Web 服务器层只会将瓶颈转移到应用程序服务器。扩展应用程序服务器可能比 Web 服务器更复杂和昂贵,并且通常只是将瓶颈转移到数据库服务器上,而数据库服务器的扩展更加困难和昂贵。即使你可以扩展数据库,你最终得到的是一个三角形拓扑结构,三角形中最宽的部分是网络服务器(最容易扩展),最小的部分是数据库(最难扩展)。

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

《基于空间的架构模式》

基于空间的架构特征:

  • 整体敏捷性高:整体敏捷性是对不断变化的环境做出快速反应的能力。由于处理单元(已部署的应用程序实例)可以快速启动和关闭,因此应用程序可以很好地响应与用户负载(环境变化)增加或减少相关的变化。由于该模式的小应用程序大小和动态特性,使用此模式创建的体系结构通常可以很好地响应编码更改。
  • 部署成本高:虽然基于空间的架构通常不是解耦和分布式的,但它们是动态的,复杂的基于云的工具允许应用程序轻松地“推送”到服务器,简化部署。
  • 可测试性低:在测试环境中实现非常高的用户负载既昂贵又耗时,因此很难测试应用程序的可扩展性方面。
  • 性能高:高性能是通过构建到该模式中的内存数据访问和缓存机制实现的。
  • 可扩展性高:高可扩展性来自这样一个事实,即对集中式数据库的依赖性很小或没有,因此从本质上消除了可扩展性方程式中的这一限制瓶颈。
  • 开发成本低:复杂的缓存和内存数据网格产品使得这种模式的开发相对复杂,主要是因为不熟悉用于创建此类架构的工具和产品。此外,在开发这些类型的架构时必须特别小心,以确保源代码中的任何内容都不会影响性能和可扩展性。

微服务

微服务(Microservices)是一种软件架构风格,它是以专注于单一责任与功能的小型功能区块 (Small Building Blocks) 为基础,利用模块化的方式组合出复杂的大型应用程序,各功能区块使用与语言无关 (Language-Independent/Language agnostic)的API集相互通信。

微服务的另一个对比是单体式应用程序。单体式应用表示一个应用程序内包含了所有需要的业务功能,并且使用像主从式架构(Client/Server)或是多层次架构(英语:Multitier architecture)(N-tier)实现,虽然它也是能以分布式应用程序来实现,但是在单体式应用内,每一个业务功能是不可分割的。

微服务在规划上也有所不同:

数据库

  • 每个服务都各有一个数据库,同属性的服务可共享同个数据库。
  • 所有服务都共享同个数据库,但是不同表,并且不会跨域访问。
  • 每个服务都有自己的数据库,就算是同属性的服务也是,数据库并不会共享。

事件传播

微服务中最重要的就是每个服务的独立与自主,因此服务与服务之间也不应该有所沟通。倘若真有沟通,也应采用异步沟通的方式来避免紧密的相依性问题。要达到此目的,则可用下列两种方式:

事件存储中心

这可以让你在服务集群中广播事件,并且在每个服务中监听这些事件并作处理,这令服务之间没有紧密的相依性,而这些发生的事件都会被保存在事件存储中心里。这意味着当微服务重新上线、部署时可以重播(Replay)所有的事件。这也造就了微服务的数据库随时都可以被删除、摧毁,且不需要从其他服务中获取资料。

消息队列

这令你能够在服务集群中广播消息,并传递到每个服务中。具有这个功能的像是 NSQ 或是 RabbitMQ。你能够在 A 服务上广播一个“创建新用户”的事件,这个事件可以顺便带有新用户的资料。而 B 服务可以监听这个事件并在接收到之后有所处理。这些过程都是异步处理的,这意味着 A 服务并不需要等到 B 服务处理完该事件后才能继续,而这也代表 A 服务无法获取 B 服务的处理结果。与事件存储中心近乎相似,但有所不同的是:消息队列并不会保存事件。一旦事件被消化(接收)后就会从队列中消失,这很适合用在像发送欢迎信件的时机。

服务注册 & 发现

单个微服务在上线的时候,会向服务探索中心(如:Consul)注册自己的 IP 位置、服务内容,如此一来就不需要向每个微服务表明自己的 IP 位置,也就不用替每个微服务单独设置。当服务需要调用另一个服务的时候,会去询问服务探索中心该服务的 IP 位置为何,得到位置后即可直接向目标服务调用。 这么做的用意是可以统一集中所有服务的位置,就不会分散于每个微服务中,且服务探索中心可以每隔一段时间就向微服务进行健康检查(如透过:TCP 调用、HTTP 调用、Ping),倘若该服务在时间内没有回应,则将其从服务中心移除,避免其他微服务对一个无回应的服务进行调用。

参考文献