单体架构到微服务指南:实施步骤

91 阅读8分钟

构建微服务生态系统的目的

微服务生态系统是一个服务平台,每个服务都封装了一个业务功能。业务能力表示企业在特定领域为实现其目标和责任而执行的操作。

每个微服务都公开一个API,开发人员可以以自助方式发现和使用该API。

微服务具有独立的生命周期。开发和测试人员可以独立构建、测试和发布每个微服务。

与微服务中的一般感知和“微”相反,每个服务的大小最不重要,并且可能会根据组织的运营成熟度而有所不同。正如马丁·福勒所说,“微服务是一个标签,而不是描述”。

从哪个部分开始?

理想情况下,我们希望定位那些能给我们带来最大利益并且最容易迁移的部分:

  • 查看BUG管理系统(禅道),哪个模块BUG数量最多?
  • 检测模块依赖情况,哪个模块最小且相互依赖最少?数据可以干净地分开吗?
  • 分析您的工程,哪个模块资源使用率最高且可以从扩展中收益?

合理的实施步骤

1.预热基础架构

通过简单且相当解耦的功能进行预热

一个相对成熟的微服务基础架构包含以下能力:

  • 能按需访问不同的部署环境
  • 能持续集成、持续交付和持续部署(CI/CD)
  • 保护、调试和监控分布式服务

所以让开发人员构建一到两个简单的服务来完善底层的基础架构、CICD、接口保护、监控系统。这个服务不需要对当前正在使用整体式架构的许多核心代码进行更改,并且可能不需要数据存储。团队正在优化的是验证其交付方法,提高团队成员的技能,并构建可独立部署所需的最低基础架构。

例如,第一个可以是仅提供图片裁切、压缩功能的服务。

我建议首先使用边缘服务,因为在开始时,开发团队最大的风险是无法正确操作微服务。因此,最好使用边缘服务来实践它们所需的操作先决条件。

image.png

2.最小化依赖性

尽量不依赖回单体架构

作为一项基本原则,交付团队需要最大限度地减少新形成的微服务对整体架构的依赖性。

微服务的一个主要好处是具有快速且独立的发布周期。通常,将功能从单体架构中微服务化的主要动机是在单体中功能的高资源使用率和高代码变更速率,因此我们希望通过消除对整体架构的依赖关系来分离这些核心功能。

例如一个零售在线系统,其中“购买”和“优惠”是核心功能。“购买”在结帐过程中使用“优惠”,为客户提供他们有资格获得的最佳促销活动。如果我们需要决定接下来要解耦这两种功能中的哪一种,我建议先解耦“优惠”,然后再“购买”。按照这个顺序,“购买”首先锁定在整体式架构中,并依赖于新的“优惠”微服务

image.png

可能并不总是能避免依赖关系回到整体式架构。如果新服务最终回调到整体式架构,我建议从整体式架构中公开一个新的API,并通过新服务中的防损坏层访问该API,以确保整体式架构概念不会泄露出去。

定义反映明确的域概念和结构的API,即使当前整体式架构的内部实现可能并非如此(思考未来微服务化后的接口定义)。

image.png

3.拆分粘性功能

整体架构中,有一到两个功能通常会被其他功能集中依赖,开发人员需要识别这些粘性功能,将其解构为定义良好的域概念,然后将这些域概念微服务化。 例如,在基于Web的整体式架构中,“用户Session”的概念是最常见的耦合因素之一。Session通常是许多用户属性的存储集合(昵称、头像、地理位置),系统中的其他模块经常依赖这些属性,如果不尽快将其解耦、解构和重新定义当前“会话”的概念,将来很难解耦其他功能(假设“新聊天微服务”依赖“用户Session”,如果“用户Session”还未从整体架构中解耦出来,那么依赖将重新会到整体架构,且是双向的依赖关系)。

image.png

4.垂直解耦并尽早释放数据

绝大多数的解耦方式为提取面向前端的组件,以便为前端开发人员提供友好的API,而数据仍和整体架构存储在一个系统中。虽然这种方法能让微服务快速得到拆分和交付,同时在后续更频繁的变更代码和发布的过程中能更安全(不影响整体系统)。但在不解耦数据的情况下,架构不是微服务。将所有数据保留在同一数据存储中与微服务的分散式数据管理特征背道而驰。

垂直解耦的策略:垂直移出功能,将其数据从整体分离,并将所有前端应用程序重定向到新的API。 要分离的数据可能正在被其他多个模块读取和写入,想要做到无缝分离是很难的。开发团队需要梳理适合其环境的数据迁移策略,具体取决于他们是否能够同时重定向其他模块的读写路径和保证新旧系统的数据一致性。

Stripe四阶段数据迁移策略适用于许多需要增量迁移数据的应用程序的环境,而所有处于更改状态的系统都需要持续运行。

image.png

避免仅解耦代码、从不解耦数据的反模式

5.将业务重要内容与频繁更改的内容分离

开发人员需要根据他们获得的好处来不断评估解耦的成本,例如开发团队的目标是更快交付速度,那么就必须确定修改得最多的功能,解耦代码中不断变化的部分。交付团队可以分析git代码提交数据,找出历史上变化频繁的模块,并将其与产品路线图综合分析找到交集,以了解在不久的将来会频繁变更的功能。

image.png

“活动”是一个需要不断推出新玩法来吸引用户参与的业务,所以“活动模块”的代码会经常被修改。

6.解耦功能而非代码

默认情况下,微服务化被想象为按原样重用现有实现,并将代码提取到单独服务。部分原因是我们对设计和编写的代码有认知偏见,这被称为“宜家效应”。这种偏见将导致开发团队忽视提取和重用代码的高成本和低价值。

宜家效应:开发者对于所编写的代码付出的劳动(情感)越多,就越容易高估该代码的价值。

所以,更好的办法是交付团队可以选择重写功能并停用旧代码。重写使他们有机会重新审视业务功能,与业务需求方沟通,以简化遗留流程。它还为技术更新提供了机会,使用最适合该特定服务的编程语言和技术堆栈实现新服务。 以下情况最好重写为新服务并停用旧代码:

  • 用大量的样板来实现Getter/Setter(利用注解代替)、获取数据库连接和声明、设置参数、获取结果集等所有JDBC操作(利用持久层框架代替)。
  • 经过多次更改迭代的历史遗留代码可能具有很低的可读性和低重用性(N手代码)。

除非你的代码是申请了知识产权专利的,否则还是建议重写和停用旧代码。

image.png

7.先做宏观,再做微观

微服务的规模取决于交付(开发)和运维(SRE)团队可以独立发布,监控和运维的服务数量。从围绕业务领域概念的大型服务开始,并在团队准备好操作时将服务分解为多个服务。

例如,开发人员可以从微服务“购买”开始,该服务既封装了“购物车”的内容,也封装了“支付”的能力。随着业务体量的提升和团队规模的扩张,未来可以将“购物车”与“支付”分离为单独的服务。

image.png