拆分单体应用

我们将讨论的重构的最后一个方面是从单体架构转向微服务架构。虽然有一些大型公司成功地使用单体架构,但技术社区的共识是,微服务架构在扩展和维护方面更为高效,尤其是在多团队协作时。因此,讨论如何以及何时在重构过程中拆分服务是非常重要的。

什么是单体应用?

单体应用是一个整体构建和发布的应用程序。通常,单体架构指的是一个大型应用程序,具有许多不同的职责,服务于不同的用户旅程。

图 7.5 展示了单体应用的一些优点和缺点:

image 2025 01 04 18 38 28 896
Figure 1. Figure 7.5 – Pros and cons of monolithic applications

单体应用的优缺点包括从部署到弹性等多个方面:

  • 由于单体应用是作为一个整体构建和部署的,开发者会发现它们很容易部署。然而,由于所有组件一起部署,它们也无法单独扩展,这使得应用程序的扩展变得更加困难。这可能成为影响应用程序能处理多少请求的瓶颈,从而影响企业的收入。将单体应用整体扩展也可能会非常昂贵,因为所有资源必须一起扩展。

  • 单体应用的认知负担较低,因为它们的所有代码都位于一个单一且可搜索的代码库中。在项目初期,开发者在一个单一代码库中开发更为简单。然而,随着项目的发展和团队的增长,代码库会受到初始架构、设计和技术选择的限制,导致开发速度变慢。

  • 由于只需启动一个应用程序,工程师发现单体应用易于测试和调试。然而,由于所有模块都托管在一起,错误可能会导致整个应用程序宕机。这再次可能对企业的收入造成重大影响。

许多组织在团队和代码库较小时选择单体架构。随着团队和应用程序功能的增长,他们发现很难扩展和维护这些单体应用。为了缓解单体开发带来的缺点,一种新的应用程序构建方法应运而生。

什么是微服务架构?

微服务架构是一种系统设计方法,依赖于独立构建和发布的服务,这些独立的单位被称为微服务。每个微服务都有自己的职责和自包含的资源来完成它们的目标。

图 7.6 展示了微服务架构的一些优点和缺点:

image 2025 01 04 18 39 05 660
Figure 2. Figure 7.6 – Pros and cons of microservice architectures

微服务架构的优点解决了单体应用的许多缺点:

  • 由于每个微服务都是独立部署的,这种架构提供了灵活的扩展性。我们可以根据哪些用户旅程最受欢迎来扩展应用程序的某个部分。然而,由于每个服务都有自己专用的资源,微服务架构的基础设施成本可能较高。

  • 微服务的小型代码库更易于维护,尤其是在重构时。它们还可以做出自己的技术选择,提供给工程师选择适合每个微服务目标的最佳工具的机会。然而,不同代码库之间的分离要求更高的组织开销,以确保各个单元能够正确协同工作,并遵循统一的工程标准。

  • 由于每个微服务都有明确的功能和职责,它们需要较小范围的测试来确保它们的正确性。然而,集成点成为测试的焦点,使得集成测试比以往任何时候都更加重要。当系统性错误发生时,每个服务都有自己的日志,这使得这种架构更难调试。

图 7.7 展示了这两种系统类型:

image 2025 01 04 18 39 25 946
Figure 3. Figure 7.7 – Monolithic application versus microservice architecture
  • 单体应用包含了实现所有功能的组件,并依赖于一个数据库。

  • 微服务架构将单体应用根据所提供的功能拆分开来,这些功能彼此依赖,以交付与单体应用相同的用户旅程。

如我们所见,没有任何一种开发方式是完美的。单体应用在应用程序流量可预测或团队有严格架构设计指导方针的情况下可能工作得很好,但大多数组织都会发展团队并扩展产品。在这种情况下,组织通常会选择微服务架构。因此,工程师必须了解如何开发和测试微服务架构,这对于应对现代复杂应用和多团队协作至关重要。

关键的重构考虑因素

许多组织已经开始将他们的单体应用拆分为微服务架构。工程社区也讨论了如何尽可能无痛且成功地进行这项工作。我们可以强调一些进行这种重构时需要考虑的关键点。

定义边界

为了成功实施微服务,微服务需要有自己的领域,并且有明确的应用边界。工程团队可以通过分析或生成的依赖图来识别应该从单体应用中提取的部分。

基于这些,团队可以确定以下内容:

  • 微服务将负责的功能和领域模型。例如,在一个电子商务应用中,我们可能会识别出一个服务,负责将物品添加到购物车并进行管理。

  • 微服务所需的上游依赖。例如,前面提到的购物车服务将依赖于库存服务,库存服务会告诉它商品的价格以及是否有库存。

  • 微服务所需的下游依赖和数据存储解决方案。例如,购物车服务将把数据保存到内存数据结构存储(如 Redis),并且在用户决定购买购物车中的商品后,可能依赖结账服务。

边界识别的结果可能是微服务基础设施需求的高层设计,以及该微服务将暴露给系统其他部分的 API 概述。

松耦合

微服务架构的一个优势是它允许我们构建松耦合的服务,既在开发角度也在部署角度。然而,如果团队没有隔离服务间的依赖,这个优势可能很容易丧失。

微服务中的松耦合是什么?

在微服务架构中,如果一个服务的设计或实现的更改不会导致它所依赖的其他服务或依赖它的服务发生更改,那么这些服务就是松耦合的。

松耦合的微服务应该遵循以下经验法则:

  • 拥有独立的数据存储:当微服务共享同一个数据库时,它们也共享同一个故障点。共享数据库的故障将导致所有依赖于它的服务一起失败。

  • 使用异步通信:使用如队列和事件总线等异步通信模式将数据传递给其他服务。这使我们能够独立扩展服务,甚至在适当的时候批量处理消息。

  • 实现容错能力:在设计服务时,假设其内部和外部的依赖可能会失败或响应缓慢。REST API 中常见的实现方式是 断路器模式,当检测到失败模式时,断路器会超时外部调用,以确保微服务能够继续运行,并在其某个依赖不可用时使用默认值。

  • 进行向后兼容的更改:尽可能使微服务的 API 进行向后兼容的更改,这样就不会强迫其他服务在同一时间内实现更改其有效负载。

  • 实现请求追踪和服务监控:在微服务架构中,了解请求如何流经系统至关重要。这有助于检测系统中的错误并识别基础设施资源不足的服务。

在微服务设计和实现过程中遵循这些规则将帮助你真正发挥微服务的优势。在第 8 章《微服务架构的测试》中,我们将进一步讨论这些原则,并探讨如何调整我们的测试策略以适应微服务架构的挑战。

微服务在没有其依赖时会如何表现?

一旦我们识别了微服务的依赖关系,我们还应记得设计和测试微服务在其依赖遇到故障时的表现。这是微服务设计中一个重要的部分,称为 优雅降级,不可忽视。