本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。
strangler fig 模式
意图
Strangler fig 模式有助于逐步将单体应用程序迁移到微服务架构,从而降低转型风险和业务中断。
动机
开发单片应用程序是为了在单个流程或容器中提供其大部分功能。代码紧密耦合。因此,应用程序变更需要进行彻底的重新测试,以避免出现回归问题。无法单独测试这些更改,这会影响周期时间。随着应用程序的功能越来越丰富,高度复杂性会导致更多的维护时间,增加上市时间,从而减缓产品创新。
当应用程序规模扩大时,它会增加团队的认知负担,并可能导致团队所有权界限不明确。根据负载扩展单个功能是不可能的,必须对整个应用程序进行扩展以支持峰值负载。随着系统的老化,该技术可能会过时,从而推高支持成本。整体式传统应用程序遵循开发时可用的最佳实践,这些实践并不是为分发而设计的。
当单体应用程序迁移到微服务架构时,可以将其拆分为较小的组件。这些组件可以独立扩展,可以独立发布,也可以由各个团队拥有。这会导致更快的变化速度,因为变更是局部化的,可以快速测试和发布。变更的影响范围较小,因为组件是松散耦合的,可以单独部署。
通过重写或重构代码将整体完全替换为微服务应用程序是一项艰巨的任务,也是一个很大的风险。大爆炸式迁移,即在单个操作中迁移整体结构,这会带来转型风险和业务中断。在重构应用程序的同时,添加新功能极其困难,甚至是不可能的。
解决这个问题的一种方法是使用马丁·福勒引入的 strangler fig 模式。这种模式涉及通过逐步提取功能并围绕现有系统创建新应用程序来转向微服务。整体结构中的功能逐渐被微服务所取代,应用程序用户可以逐步使用新迁移的功能。当所有功能都移至新系统时,可以安全地停用单片应用程序。
适用性
在以下情况下使用 strangler fig 图案:
-
您想逐步将单体应用程序迁移到微服务架构。
-
由于巨石的规模和复杂性,大爆炸式迁移方法是有风险的。
-
该企业想要添加新功能,迫不及待地想完成转型。
-
在转型期间,必须将最终用户受到的影响降至最低。
问题和注意事项
-
代码库访问权限:要实现 strangler fig 模式,你必须有权访问整体应用程序的代码库。随着功能迁移出整体结构,您将需要对代码进行细微的更改,并在整体结构中实现反腐层,以将调用路由到新的微服务。如果没有代码库访问权限,则无法拦截呼叫。代码库访问对于重定向传入请求也至关重要,可能需要进行一些代码重构,以便代理层可以拦截对迁移功能的调用并将其路由到微服务。
-
域不清晰:系统过早分解可能代价高昂,尤其是在域名不清晰并且有可能弄错服务边界的情况下。域驱动设计 (DDD) 是一种理解域的机制,而事件风暴是一种确定域边界的技术。
-
识别微服务:您可以使用 DDD 作为识别微服务的关键工具。要识别微服务,请寻找服务类别之间的自然划分。许多服务将拥有自己的数据访问对象,并且可以很容易地分离。具有相关业务逻辑的服务和没有依赖关系或几乎没有依赖关系的类是微服务的理想选择。你可以在分解整体结构之前重构代码,以防止紧密耦合。您还应该考虑合规性要求、发布节奏、团队的地理位置、扩展需求、用例驱动的技术需求以及团队的认知负荷。
-
反腐败层:在迁移过程中,当整体结构中的功能必须调用作为微服务迁移的功能时,您应该实现一个反腐败层 (ACL),将每次调用路由到相应的微服务。为了分离整体结构中的现有呼叫者并防止对其进行更改,ACL 可以用作适配器或外观,将呼叫转换为较新的接口。本指南前面的 ACL 模式的 “实现” 部分对此进行了详细讨论。
-
代理层故障:在迁移期间,代理层会拦截发送到单体应用程序的请求,并将它们路由到旧系统或新系统。但是,此代理层可能会成为单点故障或性能瓶颈。
-
应用复杂性:大型巨石最能从勒死无花果模式中受益。对于完全重构的复杂性较低的小型应用程序,在微服务架构中重写应用程序可能比迁移应用程序更有效。
-
服务交互:微服务可以同步或异步通信。当需要同步通信时,请考虑超时是否会导致连接或线程池消耗,从而导致应用程序性能问题。在这种情况下,对于可能长时间失败的操作,使用断路器模式可立即返回故障。异步通信可以通过使用事件和消息队列来实现。
-
数据聚合:在微服务架构中,数据分布在数据库之间。当需要数据聚合时,可以在前端使用,或者AWS AppSync
在后端使用命令查询责任分离 (CQRS) 模式。 -
数据一致性:微服务拥有自己的数据存储,单体应用程序也可能使用这些数据。要启用共享,您可以使用队列和代理将新微服务的数据存储与整体应用程序的数据库同步。但是,这可能会导致数据冗余以及两个数据存储之间的最终一致性,因此我们建议您将其视为战术解决方案,直到您可以建立长期解决方案(例如数据湖)。
实施
在 strangler fig 模式中,您可以将特定功能替换为新的服务或应用程序,一次只能使用一个组件。代理层拦截发送到单体应用程序的请求,并将它们路由到旧系统或新系统。由于代理层会将用户路由到正确的应用程序,因此您可以向新系统添加功能,同时确保整体架构继续运行。新系统最终取代了旧系统的所有功能,您可以将其停用。
架构简析
在下图中,单体应用程序有三种服务:用户服务、购物车服务和账户服务。购物车服务依赖于用户服务,应用程序使用单体关系数据库。

第一步是在店面用户界面和单体应用程序之间添加代理层。一开始,代理会将所有流量路由到单体应用程序。

当你想向应用程序添加新功能时,可以将它们作为新的微服务来实现,而不是向现有的整体结构中添加功能。但是,为了确保应用程序的稳定性,您可以继续修复整体结构中的错误。在下图中,代理层根据 API URL 将调用路由到整体或新的微服务。

添加反腐败层
在以下架构中,用户服务已迁移到微服务。购物车服务调用用户服务,但在整体架构中不再提供该实现。此外,新迁移的服务的接口可能与其先前在单体应用程序中的接口不匹配。为了应对这些变化,您需要实施一个 ACL。在迁移过程中,当整体架构中的功能需要调用作为微服务迁移的功能时,ACL 会将调用转换为新接口并将其路由到相应的微服务。

您可以在整体应用程序中将 ACL 实现为特定于已迁移服务的类;例如,UserServiceFacade
或。UserServiceAdapter
在所有依赖服务都迁移到微服务架构后,必须停用 ACL。
使用 ACL 时,购物车服务仍会调用整体结构中的用户服务,而用户服务通过 ACL 将呼叫重定向到微服务。购物车服务仍应在不知道微服务迁移的情况下调用用户服务。这种松散耦合是减少回归和业务中断所必需的。
处理数据同步
作为最佳实践,微服务应拥有自己的数据。用户服务将其数据存储在自己的数据存储中。它可能需要将数据与整体数据库同步,以处理报告等依赖关系,并支持尚未准备好直接访问微服务的下游应用程序。单体应用程序可能还需要其他尚未迁移到微服务的功能和组件的数据。因此,需要在新的微服务和整体架构之间进行数据同步。要同步数据,可以在用户微服务和整体数据库之间引入同步代理,如下图所示。每当更新队列的数据库时,用户微服务都会向队列发送一个事件。同步代理监听队列并持续更新整体数据库。对于正在同步的数据,整体数据库中的数据最终会保持一致。

迁移其他服务
当购物车服务从单体应用程序中迁移出来时,其代码会被修改为直接调用新服务,因此 ACL 不再路由这些呼叫。下图阐明了此架构。

下图显示了最后的扼杀状态,其中所有服务都已迁移出巨石,只剩下巨石的骨架。历史数据可以迁移到各个服务拥有的数据存储中。前交叉韧带可以移除,此时巨石已准备好退役。

下图显示了单片应用程序停用后的最终架构。您可以根据应用程序的要求通过基于资源的 URL(例如http://www.storefront.com/user
)或通过其自己的域(例如http://user.storefront.com
)托管各个微服务。有关使用主机名和路径 APIs 向上游使用者公开 HTTP 的主要方法的更多信息,请参阅 API 路由模式部分。

使用 AWS 服务来实施
使用 API Gateway 作为应用程序代理
下图显示了单片应用程序的初始状态。假设它是 AWS 通过使用 lift-and-shift策略迁移到的,因此它在亚马逊弹性计算云 (Amazon EC2) 实例上运行,并使用亚马逊

在以下架构中,在单体应用程序前AWS Migration Hub Refactor Spaces部署 Amazon API Gatew

用户服务迁移到 Lambda 函数中,亚马逊 DynamoDB 数据库存储

在下图中,购物车服务也已从整体迁移到一个 Lambda 函数中。重构空间中添加了额外的路由和服务终端节点,流量会自动切换到 Lamb Cart
da 函数。Lambda 函数的数据存储由亚马逊管理。 ElastiCache

在下图中,最后一个服务(账户)从整体架构迁移到 Lambda 函数中。它继续使用最初的 Amazon RDS 数据库。新架构现在有三个带有独立数据库的微服务。每种服务使用不同类型的数据库。这种使用专门构建的数据库来满足微服务的特定需求的概念被称为多语言持久性。Lambda 函数也可以用不同的编程语言实现,具体取决于用例。在重构期间,重构空间会自动将流量切换和路由到 Lambda。这可以为您的建筑商节省架构、部署和配置路由基础设施所需的时间。

使用多个账户
在之前的实现中,我们使用了具有私有子网和公有子网的单个 VPC 作为整体应用程序,为了简单起见,我们在同 AWS 账户 一个虚拟私有子网中部署了微服务。但是,在现实场景中,这种情况很少见,在这些场景中, AWS 账户 为了独立于部署,微服务通常被分成多个部署。在多账户结构中,您需要配置从单体到不同账户的新服务的路由流量。
重构空间可帮助您创建和配置 AWS 基础架构,以便将 API 调用从单体应用程序中路由出去。作为其应用程序资源的一部分,重构空间在您的 AWS
账户内编排 API Gat
假设用户和购物车服务部署到两个不同的账户,如下图所示。使用重构空间时,您只需要配置服务端点和路由。重构空间可自动执行 API Gateway—Lambda 集成和 Lambda 资源策略的创建,因此您可以专注于安全地从整体上重构服务。

有关使用重构空间的视频教程,请参阅使用增量重构应用程序