在本文中,我们将学习如何使用设计模式、原则和最佳实践来设计微服务架构。我们将使用适当的架构设计模式和技术。
在本文结束时,您将了解如何通过设计系统来处理数百万个请求,以在微服务分布式架构上实现高可用性、高可扩展性、低延迟和网络故障恢复能力。
事件驱动架构
本课程将是一个循序渐进的软件架构设计之旅,从单体架构演变为事件驱动的微服务架构。
我们将从软件架构的基础开始,设计可以处理少量请求的单一电子商务架构。
设计架构之旅
之后,架构将逐步演进如下:
分层架构
面向服务的架构 (SOA)
微服务
最后是事件驱动的微服务架构
最终状态将提供按照这种方法处理数百万个请求的能力。
本文将提供理论和实践信息。
我们将学习一个特定的模式,为什么以及我们应该在哪里使用
之后,我们将看到应用这些模式的参考架构
之后,我们将通过一起应用这个新学习的模式来设计我们的架构
最后,我们将根据这些架构决定使用哪些技术。
因此,我们将从单体架构迭代并演进到事件驱动的微服务架构。
我们将根据以下问题改进这些架构:
我们如何扩展应用程序?
我们的应用程序需要处理多少请求?
我们的架构可以接受多少秒的延迟?
因此,我们根据以下问题演变这些问题:
非功能性需求
可扩展性和可靠性是衡量您的应用程序为最终用户服务的表现的指标。如果我们的电子商务应用程序可以在不出现明显停机的情况下处理数百万用户,那么我们可以说该系统具有高度可扩展性和可靠性。可扩展性和可用性可能是设计良好架构时的相关特征。
可扩展性:电子商务应用程序应该能够为数百万用户提供服务
可用性:电子商务应用程序应该 24/7 全天候可用
可维护性:电子商务应用程序的维护不应该很复杂。
效率:电子商务应用程序应以可接受的延迟响应,小于 2 秒。
好的,让我们谈谈可接受的延迟;如果我们的应用程序被越来越多的用户使用,我们如何才能使我们的应用程序具有可接受的延迟?
有关详细信息,请参见下表:
每秒请求和可接受的延迟
正如您在表中所见,我们将启动一个小型电子商务应用程序,该应用程序每秒仅获得 2.000 个并发用户和 500 个请求。
我们将根据这些预期数量设计我们的电子商务架构。
之后,当我们的业务增长时,将需要更多的资源来满足更多的请求,我们将看到如何根据这些数字改进我们的架构。
许多方法和模式在数十年的软件开发过程中演变而来,它们都有好处和挑战。
因此,我们将开始了解现有的方法来构建我们的电子商务应用程序、发展并将其转移到云中。要了解云原生微服务,我们需要了解什么是单体应用程序以及它们如何引导我们从单体服务转向微服务。
单体架构
谈到遗留应用程序,我们可以说大多数遗留应用程序都是作为单体架构实现的。
如果所有项目功能都存在于单个代码库中,则该应用程序被称为单体应用程序。整体模式包括同一代码库中的用户界面、业务代码和数据库调用的所有内容。
所有应用程序工件都包含在一个巨大的部署中。即使是单体应用程序也可以在不同的层进行设计,例如表示层、业务层和数据层,然后将该代码库部署为单个 jar/war 文件。
我们将在后面讨论的单体方法有几个优点。但让我详细说明一些主要优点和缺点:
由于它是一个单一的代码库,因此很容易拉取并启动一个项目。
由于此项目结构包含在一个项目中,并且易于调试跨不同模块的交互。
由于活动部件较少,维护和故障排除的复杂性也较低。
不幸的是,单体架构有很多缺点,我们可以列出其中的一些:
随着时间的推移,代码量会变得太大;管理具有挑战性。
很难在同一个代码库中并行工作。
难以在遗留的大型单体应用程序上实现新功能
任何更改都需要部署整个应用程序的新版本,等等。
如您所见,我们更好地理解了单体架构的优缺点。
单体架构有很多缺点,但如果您正在构建一个小型应用程序,单体架构是您可以应用于您的项目的最佳架构之一。因为,在很多方面,单体应用程序很简单。
单体架构为以下方面提供了简单性:
构建
测试
部署
疑难解答
垂直缩放(放大)
与微服务相比,它的开发很简单,微服务需要熟练的开发人员来识别和开发服务。它更容易部署,因为只有一个 jar/war 文件可用于部署完整的应用程序。
在本节中,我们将逐步使用单体架构设计我们的电子商务应用程序。我们会根据需求一个一个的迭代架构设计。
我们应该始终从写下 FR(功能需求)和 NFR(非功能需求)开始。
列出产品
按品牌和类别过滤产品
将产品放入购物车
申请折扣券并查看购物车中所有商品的总费用
检查购物车并创建订单
列出我的旧订单和订单项目历史
可扩展性
由于并发用户的增加,性能可接受
此外,最好将原则添加到我们的图片中,以便始终记住它们。
KISS——保持简单,愚蠢
YAGNI——你不会需要它
我们将在设计架构时考虑这些原则。
设计单体架构
如您所见,我们使用单体架构设计了我们的电子商务应用程序。
我们增加了电子商务大盒子;这些是我们的电子商务应用程序的组件:
购物界面
目录服务
购物车服务
折扣服务
订购服务
如您所见,此传统 Web 应用程序的所有模块都是容器中的单个工件。
这个单体应用程序有一个包含所有模块的庞大代码库。
如果您向该应用程序引入新模块,则必须对现有代码进行更改,然后将具有不同代码的工件部署到应用程序服务器,例如 Tomcat 服务器。我们遵循我们的 KISS 原则,即保持简单。
我们将根据要求重构我们的设计,并逐步迭代。
从下图中可以看出,我们通过添加两个应用程序服务器并在客户端和电子商务应用程序之间的单体应用程序前面放置一个负载均衡器,通过水平扩展对单体架构进行了扩展。
为了在单体架构上提供可扩展性,我们需要增加电子商务应用程序服务器的实例数量,并在我们的应用程序前面提供一个新的负载平衡器。
Load Balancer 将使用一致的分配算法容纳请求并将请求发送到我们的电子商务应用程序服务器。此功能将为服务器提供平均负载。
我们将做出一些技术选择——适配技术栈。
从图中可以看出,我们已经为我们的整体电子商务应用程序选择了可能的选项。NGINX 是负载平衡的绝佳选择,Oracle Java 也是此类应用程序的标准实现。
微服务是可以协同工作并且可以自主/独立部署的小型业务服务。
来自 Martin Fowlers 微服务文章;
微服务架构风格是一种将单个应用程序开发为一组小型服务的方法,每个服务都在自己的进程中运行并与轻量级机制(通常是 HTTP 或 gRPC API)进行通信。
因此,我们可以说微服务架构是一种云原生架构方法,其中应用程序由许多松散耦合且可独立部署的较小组件组成。让我们描述一些微服务特征:
他们有自己的技术栈,包括数据库和数据管理模型。
通过 REST API、事件流和消息代理相互通信。
它们按业务能力组织,用线分隔服务,通常称为有界上下文。
在接下来的部分中,我们还将看到如何将微服务与有界上下文分离。
微服务是小型的、独立的、松散耦合的。一小组开发人员可以编写和维护服务。每个服务都是一个单独的代码库,一个小型开发团队就可以管理它。
这些服务可以独立部署。团队可以更新现有服务而无需重建和重新部署整个应用程序。
每个服务负责保存其数据或外部状态。此功能不同于传统模型,在传统模型中,单独的数据层处理数据持久性。
敏捷性——微服务的基本特征之一是服务更小且可独立部署。
小型、专注的团队——微服务应该足够小,以供单个功能团队构建、测试和部署。
可扩展性——微服务可以独立扩展,因此您可以扩展需要较少资源的子服务,而无需扩展整个应用程序。
复杂性——微服务应用程序有许多服务需要协同工作并创造价值。由于有许多服务,因此移动部件比单体应用程序多。
网络问题和延迟——由于微服务很小并且通过服务间通信进行通信,我们应该管理网络问题。
数据完整性——每个微服务都有自己的数据持久性。因此,数据一致性可能是一个挑战。
在本节中,我们将逐步设计微服务架构。根据需求逐一迭代架构设计。
设计微服务架构
我们在设计微服务架构时遵循了Database-per-Services Pattern,并为每个微服务建立了一个数据库。微服务是从具有分离的独立服务的整体应用程序模块中分解出来的。
所以现在,这些数据库可以是多语言持久性。也就是说Product微服务可以使用NoSQL文档数据库,Shopping Cart微服务可以使用NoSQL键值对数据库,Order微服务可以使用Relational数据库,满足每个微服务的数据存储需求。
让我们看看微服务架构图,考虑一下这个架构缺少什么?这种架构的痛点是什么?我们如何才能将此体系结构发展为更好的体系结构,使其具有更高的可扩展性、可用性并能够容纳更多并发请求?
前端和后端紧密耦合
看到前端 UI 元素和微服务通信是直接的,管理所有这些连接点似乎很复杂。现在我们应该关注微服务通信!
更改通信机制是迁移到基于微服务的应用程序时面临的最大挑战之一。
从本质上讲,微服务是分布式的;微服务通过网络级别的服务间通信相互通信。每个微服务都有它的实例和进程。
因此,服务必须使用服务间通信协议(如 HTTP、gRPC 或使用 AMQP 协议的消息代理)进行交互。
由于微服务是独立开发和部署的服务的复杂结构,我们应该考虑通信类型并将它们管理到设计阶段。
如果您想设计和构建具有多个客户端应用程序的复杂的大型基于微服务的应用程序,建议使用 API 网关模式。
该模式提供了一个反向代理来将请求重定向或路由到您的内部微服务端点。API 网关为客户端应用程序提供单一端点,并在内部将请求映射到内部微服务。我们应该在客户端和内部微服务之间使用 API 网关。
API 网关可以处理授权等一般技术问题,因此无需在每个微服务中编写相同的功能,授权可以使用 API 网关以集中方式处理并发送到内部微服务。
此外,API 网关管理到内部微服务的路由,并可以在对客户端的一次响应中聚合多个微服务请求。
总之,API 网关将放置在客户端应用程序和内部微服务之间,充当反向代理并将客户端请求路由到后端服务。它还提供通用技术问题,如身份验证、SSL 终止和缓存。
我们将通过添加 API 网关模式来迭代我们的电子商务架构。
使用 API 网关
您可以看到在单个入口点收集客户端请求并将请求路由到内部微服务的图像。
此功能将处理客户端请求并路由内部微服务,将多个内部微服务聚合到单个客户端请求中,并执行横切关注点,如身份验证和授权、速率限制和节流等。
我们会继续演进我们的架构,但是请看看现在的设计,考虑一下我们如何改进设计?
这里有多个客户端应用程序连接到一个 API 网关。我们应该小心这种情况,因为如果我们在这里放置一个 API 网关,则可能包含与单点故障相关的风险。
如果这些客户端应用程序增加或添加更多逻辑到 API 网关中的业务复杂性,那将是一种反模式。因此,我们应该使用前端后端模式 (BFF) 来解决这个问题。
前端模式的后端可以根据特定的前端应用程序分离 API 网关。因此,我们有多个前端应用程序使用的后端服务,在它们之间,我们包括用于处理、路由和聚合操作的 API 网关。
但这会造成单点故障。为了解决这个问题,BFF 开启了创建多个 API Gateway 的可能性,将客户端应用按照边界分组,拆分到不同的 API Gateway 中。
好朋友模式
单一且复杂的 API 网关可能存在风险,并成为我们架构中的瓶颈。较大的系统通常通过按功能(如移动、Web 和桌面)对客户端类型进行分组来公开多个 API 网关。当您避免为多个接口定制单个后端时,BFF 模式很有用。
因此,我们应该根据用户界面创建多个 API 网关。这些 API 网关最符合前端环境的需求,无需担心影响其他前端应用程序。前端后端模式为实现多个网关提供了方向。
我们将根据前端后端模式 BFF 添加更多 API 网关模式来迭代我们的电子商务架构。
API 网关和 BFF 模式
如您所见,我们已将多个 API 网关添加到我们的应用程序中。这些 API 网关最符合前端环境的需求,无需担心影响其他前端应用程序。前端后端模式提供了实现多个网关的方向。
好的,我们已经在我们的微服务架构中创建了 API 网关。所有这些同步请求都来自客户端,并通过 API 网关转到内部微服务。
但是,如果客户端请求需要多个内部微服务怎么办?我们如何管理内部微服务通信?
服务到服务通信——链式查询
在设计微服务应用程序时,我们应该注意后端内部微服务如何相互通信。最佳实践是尽可能减少服务间通信。
但是,有时,由于客户要求或请求的操作需要调用多个内部服务,我们无法减少这些内部通信。
例如,查看上面的图片并考虑这样的用例:
用户签出购物车并创建订单
那么我们如何实现这个请求呢?这些内部调用为每个微服务进行耦合;在我们的例子中,购物车、产品和定价微服务相互依赖。
如果其中一个微服务没有响应,它就不能向客户端返回数据,所以它不是容错的。如果增加了微服务的依赖和耦合,就会产生很多问题,我们无法充分发挥微服务架构的潜力。
如果客户签出购物车,这将启动一组操作。因此,如果我们尝试使用请求/响应同步消息传递模式来执行这个下订单用例,它看起来就像这张图片。
正如我们所见,对于一个客户端 HTTP 请求,我们有六个同步 HTTP 请求。因此,很明显,它会增加延迟并对我们系统的性能、可扩展性和可用性产生负面影响。
如果我们有这个用例,如果第 5 步或第 6 步失败或某些中间服务出现故障会怎样?即使它们没有宕机,它也可能很忙,一些服务无法及时得到响应,在那种情况下,高延迟是不可接受的。
那么,这种需求的解决方案是什么?
我们可以应用两种方法来解决这些问题:
将微服务通信更改为与消息代理系统异步;我们将在下一节中看到这一点。
使用服务聚合器模式在一个 API 网关中聚合一些查询操作。
为了最小化服务到服务的通信,我们可以应用服务聚合器模式。服务聚合器设计模式接收来自客户端或 API 网关的请求,将请求分派给多个内部后端微服务,然后组合结果并在一个响应结构中响应初始请求者。
服务聚合器模式
通过服务聚合器模式的实现,我们可以减少客户端和微服务之间的聊天和通信开销。
本节将通过添加服务聚合器模式 - 服务注册模式 - 微服务通信设计模式来迭代我们的电子商务架构。
聚合器和服务注册模式
如您所见,我们已经为我们的电子商务架构应用了服务聚合器模式——服务注册模式。
如果您的通信仅在几个微服务之间进行,则同步通信是合适的。但是当涉及到多个微服务需要相互调用并且可能要等待很长时间或复杂的操作直到它们完成时,我们应该使用异步通信。
微服务异步通信
否则,微服务的依赖和耦合会造成瓶颈和严重的架构问题。
如果你有多个需要相互交互的微服务,并且如果你想在没有任何依赖或松散耦合的情况下与它们交互,那么我们应该在我们的微服务架构中使用基于异步消息的通信。
因为基于异步消息的通信与事件一起工作,所以事件可以是微服务之间的一种通信形式。我们称这种通信为事件驱动通信。
发布-订阅是一种消息传递模式,它有一个消息发送者(称为发布者)和特定的接收者(称为订阅者)。
发布-订阅设计模式
因此,发布者不会直接向订阅者发送消息。相反,每个服务都在不知道有哪些订阅者的情况下将消息分类并发布到消息代理系统中。
类似地,订阅者表达兴趣并且只接收他们感兴趣的消息而不知道哪些发布者发送给他们。
在本节中,我们将通过添加用于提供微服务异步通信设计的发布-订阅消息代理来迭代我们的电子商务架构。
消息代理
如您所见,我们应用了发布-订阅消息代理——微服务异步通信设计模式。
如果我们调整我们的技术堆栈,我们可以开始考虑哪些选项可用于发布-订阅消息代理功能。您可以选择两个不错的选择:
卡夫卡
兔MQ
在单体架构中,查询不同的实体是可以接受的,因为单个数据库保存数据。跨多个表查询数据很简单;任何不需要的数据更改都可以轻松回滚。具有严格一致性的关系型数据库具有ACID(原子性、一致性、隔离性和持久性)事务保证,因此易于管理和查询数据。
但在微服务架构中,我们通常使用多语言持久化。这意味着每个微服务都有不同的存储库,包括关系数据库和 NoSQL 数据库。在执行用户交互时,我们应该设置一个策略来管理这些数据。
因此,这意味着我们在处理微服务之间的数据交互时有多种模式和实践;我们将在本节中学习这些模式和原则。
微服务是独立的,只执行特定的功能需求。在我们的例子中,电子商务应用程序具有产品、购物篮、折扣和订购微服务。这些微服务需要相互交互以执行我们的客户用例。
因此,这意味着它们需要经常相互集成。主要是,这些集成正在查询彼此服务的数据以进行聚合或执行逻辑。
CQRS 是微服务之间查询时的关键模式之一。我们可以使用 CQRS 设计模式来避免复杂的查询并消除低效的连接。CQRS 代表命令和查询责任分离。此模式将数据库的读取和更新操作分开。
要隔离命令和查询,最好将读写数据库分成两个数据库。这样,如果我们的应用程序是读密集型的,这意味着读多于写,我们可以定义一个针对查询优化的自定义数据模式。
CQRS 设计模式
物化视图模式是实现读取数据库的一个很好的例子。因为通过这种方式,我们可以避免复杂的连接和映射与预定义的细粒度数据进行查询操作。
通过这种隔离,我们甚至可以使用不同的数据库来读取和写入数据库,例如 NoSQL 文档数据库来读取和使用关系数据库来进行 CRUD 操作。
我们学习了CQRS模式,CQRS模式主要和Event Sourcing模式一起使用。将 CQRS 与事件溯源模式一起使用时,主要思想是将事件存储在写入数据库中,这将是真实事件数据库的来源。
之后,CQRS 设计模式的读取数据库使用非规范化表提供数据的物化视图。当然,这些物化视图读数据库从写数据库中消费事件并将它们转换为非规范化视图。
事件溯源模式
应用事件溯源模式是将数据保存操作更改为数据库。事件溯源模式不是在数据库中保留最新的数据状态,而是按照数据事件的顺序保存数据库中的所有事件。该事件数据库称为事件存储。
它不是更新数据记录的状态,而是将每个更改附加到事件的顺序列表中。因此,事件存储成为数据的真实来源。之后,这些事件存储转换为读取数据库。这种转换操作可以通过事件后消息代理系统的发布/订阅模式来处理。
我们将通过应用 CQRS、事件溯源、最终一致性和物化视图来设计我们的电子商务架构。
CQRS、事件溯源和最终一致性
因此,当用户创建或更新订单时,我将使用关系数据库。当用户查询单个订单或订单历史时,我将使用 NoSQL 读取数据库并在使用具有发布/订阅模式的消息代理系统同步两个数据库时使它们保持一致。
现在我们必须考虑这些数据库的技术堆栈;让我们假设使用 Microsoft SQL Server 作为写入操作的关系数据库,使用 Cassandra 作为读取操作的 NoSQL 数据库。当然,我们将使用 Kafka 将这两个数据库与发布/订阅 Kafka 主题交换同步。
如您所见,我们已经完成了微服务数据库模式的设计。让我们深入研究微服务中的这些事件驱动架构。
事件驱动的微服务架构意味着通过事件消息与微服务进行通信。我们在微服务异步通信部分看到了发布/订阅模式和 Kafka 作为消息代理系统。
我们说我们可以使用事件驱动架构来实现异步行为和松耦合结构。例如,服务通过事件消费数据,而不是在需要数据时发送请求。此功能将提供性能提升。
事件中心
而且,事件驱动的微服务架构中也有许多创新,例如使用实时消息传递平台、流处理、事件中心、实时处理、批处理、数据智能等。
所以,我们可以通过演进这个架构,让这个事件驱动的方法更加通用和实时的事件处理特性。
根据这种新的事件驱动的微服务架构,我们可以通过事件中心连接所有东西。我们可以将 Event-Hubs 看作是一个可以提供实时处理的大型事件存储数据库。
我们将使用事件驱动的微服务架构设计我们的电子商务应用程序。
事件驱动的微服务架构
现在我们可以决定在这个架构中技术堆栈。当然,我们应该选择Apache Kafka——作为事件中心和Apache Spark,用于实时和近实时的流应用程序,这些应用程序可以转换或响应数据流。
正如您所看到的,现在我们有了事件驱动的微服务架构的反应式设计。
现在我们可以问同样的问题;
多少个并发请求可以容纳我们的设计?
借助这种最新的事件驱动微服务架构,通过容器和编排器进行部署,可以以低延迟满足目标并发请求。因为这种架构是完全松散耦合的,并且是为高扩展性和高可用性而设计的。
正如您所看到的,我们已经使用设计原则和模式的各个方面来设计我们的电子商务微服务架构。现在,您可以准备好通过这些学习来设计您自己的架构,并知道如何在您的设计中使用这些模式工具箱。
来源:
https://www.toutiao.com/article/7177735037516366371/?log_from=dccc7021f13b3_1671504374835
“IT大咖说”欢迎广大技术人员投稿,投稿邮箱:aliang@itdks.com
IT大咖说 | 关于版权
由“IT大咖说(ID:itdakashuo)”原创的文章,转载时请注明作者、出处及微信公众号。投稿、约稿、转载请加微信:ITDKS10(备注:投稿),茉莉小姐姐会及时与您联系!
感谢您对IT大咖说的热心支持!