微服务拆分的原则、时机、方法以及常见问题

前言#

在平常情况下,技术架构会随着业务规模大小、团队人数多少、技术债积累速度等动态变化。当然,引起架构变化最主要的因素还是业务发展速度。

在以前的单体架构到微服务架构演进历程 文章2 等文章中,有一张架构演进的图,如下:

(巨型单体到微服务架构的演进)

这张架构演进图,说明了架构的变化和演进的总方向。演进到微服务架构,它中间有几次架构变迁和发展的过程。

当然,这张架构演进图有一个缺憾,大单体应用架构后面缺一张模块化的单体架构图,也就是说把大单体拆分为模块化的大单体。

上面图中最后面的微服务架构图,里面有很多服务,也叫微服务(拆分为更小的服务)。关于拆分也有很多问题,可以用 5W1H、6why 思考法不断追问思考:

  • 为什么要进行微服务拆分?
  • 这些微服务要怎么拆分?如果要拆分,按照什么原则拆分?按照什么策略拆分微服务?
  • 拆分成多小算合适的微服务?
  • 微服务边界如何确定?
  • 什么时候进行微服务拆分合适?

等等一系列的问题都是拆分微服务时我们需要思考的问题。

为什么要进行拆分(划分)#

为什么要进行微服务拆分,前面的文章也有讲解,微服务的优势和劣势 文章2 。微服务的优势优点:

  1. 快速编译,一个微服务代码量更小,易编译部署
  2. 维护性变高,单个微服务功能少,代码量减少,可快速修改发布,因为每个团队独立负责一块功能。新功能交付变快,可以快速开发与交付,可独立部署
  3. 扩展性变高,根据业务规模可以随时缩减/增加服务器规模,因为是独立的微服务模块
  4. 可靠性变强,可以部署很多独立的服务
  5. 业务边界清晰,按照不同业务功能拆分为多个独立的服务
  6. 研发效率提升,业务拆分后,服务模块变小,在一个团队内就可以独立编写、测试、发布,加快研发效率。
  7. 技术异构,满足不同的业务需求。

等等好处。当然拆分后,同样带来了很多挑战,在 微服务的优势和劣势 一文中也有讲解。

微服务拆分的原则有哪些#

为了更加合理的拆分微服务,在拆分微服务过程中需要遵循一些基本原则。

1、单一职责原则#

这个原则不仅在拆分微服务时会遵循,在很多软件设计中也遵循此原则。

单一职责原则指的是一个微服务只负责一个明确的业务能力,专注于做好一件事情。一个服务的内部变化不影响另外一个服务。

比如在电商系统中,订单服务只处理订单生命周期,支付服务只处理支付流程,商品服务处理商品流程。

2、高内聚,低耦合#

高内聚

产品功能和职责相关度较高的集中在一起,服务内的功能是紧密相关的,数据和行为集中。

服务间尽量减少依赖。

低耦合

各微服务之间独立存在,尽可能减少彼此依赖,服务间通过 API 或异步事件(比如用 REST API 或 消息队列来通信)来进行服务之间的交互通信,各自数据存储独立。

3、单向依赖#

微服务之间应该避免双向依赖、环形依赖。因为这样会导致服务间关系更加复杂,服务升级影响的服务过多,故障的概率变大。

如果遇到了怎么处理:

  • 1、将共同依赖的服务单独出来,做成第三方服务。通用能力下沉。
  • 2、异步解耦,比如引入消息中间件来处理。

4、数据库隔离#

每个微服务独立管理自己的数据,拥有自己独立的数据库,避免与其它服务直接共享数据库或数据存储。这样是为了避免共享数据而导致的耦合。但也增加了数据一致性管理的问题。

如果需要共享数据,通过 API 或事件与其它服务交换数据。

但是在拆分过程中往往因为业务的聚合查询需求,有时会为聚合查询独立建立一个数据存储系统。这时要注意多分析业务需求和技术选型匹配情况。

5、康威定律#

微服务划分应该与团队组织结构相匹配,确保团队的独立和自治,减少团队间的耦合工作,降低跨团队协作成本。

一个团队负责一个或多个微服务,不仅做到服务开发自治,而且每个微服务尽量独立自治没有过多依赖,一个微服务功能尽可能在一个团队内完成。

6、演进式原则#

微服务的拆分并不需要一步到位,它是一个渐进的过程,应该根据业务发展情况选择合适的架构风格然后进行拆分。

逐步拆分,持续演进。应该避免一下子拆分太多的服务带来架构复杂度急剧升高。

我们把大单体应用拆分为微服务架构时,可以分阶段分步骤进行拆分。

比如先垂直拆分为多个应用,再把每个应用拆分为比较大的模块,再把大的模块进一步拆分为小的服务。

比如可以先进行粗粒度的划分,然后在根据业务发展、研发情况的需要在进行更细粒度的拆分。

比如可以先从不太重要的业务功能进行服务拆分的实践,然后逐步推广。

比如可以先从最容易拆分的部分开始,如将日志服务或配置服务拆分为独立的微服务,然后逐步拆分其他部分。

中间还可能因为服务拆分过细,会进行服务的合并,合并为更大粒度的服务。所以微服务的拆分有时会是一个反复的过程。

"合久必分,分久必合"。

引入微服务架构和服务拆分的时机#

微服务拆分的时机,其实就是什么情况下引入微服务架构比较合适。我前面有几篇文章可供参考:

划分微服务的时机,什么时候引入微服务架构合适,其实是需要综合考量:

  • 第一:业务发展阶段
  • 第二:业务复杂度
  • 第三:开发人员情况
  • 第四:业务形态

具体情况请看上面的 3 篇文章。

微服务拆分的维度和方法#

微服务拆分维度可以从:

  • 1、业务、技术、数据、组织结构 这 4 个大维度进行思考。

  • 2、功能性非功能性 维度,这个跟上面 1 差不多,功能性纬度就是业务功能、技术功能(比如消息推送系统、监控系统),非功能性一般与技术紧密相关(比如扩展性、高性能、安全性、技术异构等)。

业务维度#

从业务纬度来划分业务领域,界定好业务领域边界。

第一种拆分方法:业务功能和业务流程的分析来进行业务领域的划分#

根据业务功能和业务流程的分析来进行业务领域的划分,从而达到划分微服务的目的。

根据业务流程和功能来划分业务领域。这需要我们熟悉整个业务全貌,每个业务功能,功能的流程 - 业务核心流程、子流程等。如果不熟悉,就需要进行业务分析。

业务分析的一般方法,比如下面这 3 种:

  • 场景分析法:从业务的场景入手分析。

  • 用例分析法:从处理业务的角色入手分析。

  • 流程分析法:从业务流程入手分析。比如从 A 到 B,要经过哪些步骤,先做什么,后做什么,有什么规则。

其中场景分析法和用例分析法有点相似,都是各种角色在各种场景下进行作业或活动的交互过程,区别是入手分析角度不同,一个是场景角度,一个是角色角度。

业务和流程分析完,就要进行业务领域的划分。

比如:大家熟悉的电商购物流程:

用户浏览商品 -> 点击购买 -> 加入购物车(或直接支付) -> 支付 -> 生成订单

根据这个购物的流程,可以划分为用户域、商品域、订单域、支付域等业务领域。

第二种拆分方法:根据DDD领域驱动设计来进行业务领域的划分#

在 DDD(领域驱动设计)方法中,有一些关于对复杂业务领域进行分析的相关概念:

  • 领域、核心域、通用域、支撑域、子域
  • 界限上下文(Bounded Context)
  • 上下文映射(Context Mapping)
  • 统一语言(Ubiquitous Language)
  • 实体、值对象、聚合、聚合根、领域对象、领域服务
  • 领域建模

这些概念对于业务领域的划分能够起到指导作用。当然还需要结合 DDD 领域驱动设计里的其他概念一起使用。

要做好业务领域的划分,最重要的是要建立领域模型。

领域模型是对某个有边界的业务领域的一个抽象,反映了业务、领域内用户和用户活动,不涉及技术。领域模型确保了领域内的业务逻辑都在一个模型内。

怎么建立领域模型,领域建模常用的方法有哪些?

  • 事件风暴(Event Storming)

  • 四色建模

  • 用例分析法

事件风暴介绍

事件风暴是一种以工作坊的方式对复杂业务进行分析探索的高效协作方法,通过快速、可视化的方式梳理业务逻辑,识别领域事件、命令、聚合等关键元素。它起源于 DDD,旨在通过跨职能团队的高效协作快速探索复杂业务逻辑,构建领域并划分系统边界。

"风暴"是让软件开发者和领域专家们聚集在一起进行业务分析的头脑风暴,进行交流讨论。它是以 "领域事件" 为中心来分析梳理业务流程,建立领域模型。

事件风暴通过不同颜色的便签(或卡片)来区分元素,事件风暴的一些核心概念:

  • 领域事件:已发生的业务事实,发生有意义的业务事件。以 "名词 + 动词被动式" 命名,反映业务状态变化。例如:订单已创建

  • 命令:触发事件的动作。通常由用户或系统事件触发。以"动词+名词"命名 。例如:提交订单

  • 聚合/聚合根:业务逻辑的集合,包含实体和值对象,维护业务规则的一致性。例如:订单聚合(含订单详情、用户信息等)

  • 外部系统:与当前系统交互的外部依赖,如第三方服务。例如:短信平台

  • 执行者:触发命令的主体,与系统交互的人,可以是用户、系统或定时任务。例如:用户、支付网关

  • 读模型:支持决策的信息视图,为执行者提供所需的数据。例如:订单追踪视图(显示状态、时间)

  • 策略:业务规则,定义事件触发时响应逻辑,可能生成新的命令。例如:支付成功后通知用户

  • 界限上下文:业务领域的边界,划分不同领域模型的范围,通常对应微服务边界

可以看出这里的概念和DDD中的很多概念相似或相同。

事件风暴实施的简易步骤

1、准备工作

  • 参与者:领域专家、开发人员、产品经理等,业务和技术相关人员参加
  • 工具:多种颜色便签、白板、白板笔
  • 场地:会议室墙面,便于协作和可视化流程

2、识别领域事件

  • 流程:团队围绕业务流程头脑风暴,列出关键事件,按照时间轴来排列
  • 规则:事件需要满足 "已发生""影响业务状态变化"等条件,需排出技术事件或查询操作

3、关联命令与执行者

  • 为每个事件标注触发命令(比如用户已注册),并确定执行者(比如用户、系统等)
  • 处理异常或相反的情况,讨论产品发生失败的场景,比如支付失败

4、划分界限上下文与聚合

  • 界限上下文:根据业务语义将事件分组(如用户管理、订单处理),形成独立领域
  • 聚合设计:从命令和事件中提取名词(比如"订单" "用户"),组合为聚合,确定聚合根的边界

5、构建领域模型

  • 细化模型:定义实体、值对象、领域服务,验证模型是否支持所有业务流程
  • 输出:上下文映射图、通用或统一语言表

其余的 2 个建模方法,四色建模和用例分析,读者可自行了解。

第三种拆分方法:根据业务稳定性、变动频率#

在业务系统中,区分变和不变的部分。

不变的部分,就是稳定性相对来说比较高,可以作为通用的服务。

将频繁变动的部分也可以独立出来,作为独立的服务。

比如电商系统中的用户信息、商品信息等模块一般稳定性高。而运营类的活动和页面变动比较频繁。

第四种拆分方法:核心业务和非核心业务#

核心业务就是主要的服务,保证高可用,非核心业务遇到堵塞时可以降级处理。

这是一种比较大的粗略的划分方法,可以作为服务划分的一种思考角度。

有的也叫链路服务中的主链路服务(核心业务的服务流程)、次级链路服务。

比如电商系统:搜索商品 -> 查看商品详情 -> 加入购物车 -> 下单结算 -> 支付服务,这就是一条商品交易的主链路服务,也是商品交易最核心的服务流程,必须高可靠的运行。

比如订单查看详情就是非核心业务。

技术纬度#

如果从技术维度来划分,有哪些纬度可以考虑呢?

高性能、可靠性、完全性、扩展性、异构性、通用性、稳定性等方面来进行考虑。

还可以技术为业务提供的功能,比如消息推送系统、分布式缓存系统、监控系统等,当然这些都是比较大的系统。

高性能

将性能要求高或性能压力大的功能或模块进行拆分,避免因为性能影响其它服务,造成整个链路服务的阻塞。

比如电商应用中瞬间访问量特别大的系统:抢购系统、秒杀系统等系统。

可靠性

将可靠性要求比较高的功能或模块进行拆分,并定义为核心服务,与链路中其它非核心服务隔离开,保证核心服务的高可用性。

比如电商应用的用户购物流程中,商品交易是核心服务,交易后的查看订单详情可以作为非核心服务。商品交易的核心服务提供高可用性。

通用性

将多数业务经常用到的功能进行提炼,独立为基础服务、通用性的服务。

比如微服务中的限流、鉴权、隔离、日志收集、安全等功能。

还有一些比较大的通用性的系统,比如消息推送系统、缓存系统、监控系统等。

稳定性

将变动不大的服务、比较稳定的服务拆分为独立的服务,或频繁变动的服务也可以作为独立的服务。

组织结构纬度#

这个是拆分的纬度是根据,康威定律:

‌"设计系统的组织,其产生的设计等同于组织之间的沟通结构。"

简单来说,一个公司的软件架构往往会反映该公司的组织结构。例如:

  • 如果一家公司有多个独立的团队各自开发业务功能模块,最终的系统可能是多个独立模块服务的组合(如微服务架构,它也可以理解为用技术的手段匹配组织协作方式)。
  • 如果一家公司是一个高度集中的团队,系统可能倾向于单体架构。

‌> 康威定律强调:"如果你想改变系统架构,应该先调整组织结构。"

微服务团队人数通常控制在「两个披萨团队」(6-10人),微服务的开发、测试、部署、运行都可以在一个团队内完成,微服务自治,微服务团队也独立、自治。

数据纬度#

这个数据纬度联动业务拆分,数据随着业务拆分而拆分,拆分的业务有自己独立的数据库。

微服务拆分过程中常见问题#

1、服务粒度拆分过度、过细#

微服务"微"字,这个汉字容易让人想到很小的服务,这个"微"字只是一个形容服务粒度大小的形容词,它的含义可以是 小、中、大,拆分的服务必须与技术、业务发展、团队相适配。

问题

在微服务拆分过程中,如果过度追求"微",将单一类或简单的功能都拆分为独立的服务,会导致大量的小型服务,服务数量会爆炸,部署、调试、维护都将变得复杂,运维成本激增,跨服务调用频繁、性能下降、团队要维护服务数量多等等问题。

服务拆分过粗,未能解耦相关业务领域,单个服务臃肿,难以独立扩展。

必须平衡好服务拆分粒度与系统复杂性之间的关系。

解决方法

  • 1、遵循高内聚原则:避免服务拆分过细,一个服务应覆盖一个完整的业务领域、业务子域。

  • 2、逐步拆分和迭代改进:不必一次性拆分完成,可以从比较大的粒度开始拆分然后在演进到合适的小的粒度、或从非核心业务开始拆分,并及时的反馈调整。

2、跨服务共享数据或代码#

问题

拆分的多个服务直接读取同一个数据库表或共享代码库(比如公共 JAR 包),导致高耦合,破坏了服务的独立性和服务自治。

解决方法

  • 1、独立数据库:每个服务独立拥有自己的数据库,数据隔离。
  • 2、API访问数据:如果需要其它服务数据,通过 API 获取数据或事件订阅获取数据。

3、过早拆分或盲目的采用微服务架构#

问题

不是所有的系统都适合微服务架构。在业务需求不明确或业务发展规模还不大情况下,过早引入微服务,会造成业务交付变慢、团队负责服务数量过多造成团队负担等问题。

解决方法

  • 1、渐进式拆分:一般从单体起步,渐进式拆分。大单体->多个应用->微服务架构。

  • 2、引入微服架构的时机 :看上面的一节:引入微服务架构和服务拆分的时机

4、分布式单体(服务边界划分不清)

问题

服务虽然物理上分离了,但是逻辑上高度耦合,如共享数据、频繁同步调用,导致部署依赖多,失去了微服务的独立部署和扩展优势,维护成本不降反而增加。

解决方法

  • 1、采用领域驱动设计 DDD:在拆分服务前,采用 DDD,通过领域建模和业务流程梳理,明确服务的边界和职责,确保服务自治。

  • 2、异步通信:使用事件驱动(如Kafka)解耦服务,减少直接依赖。

5、一次性拆分和忽略架构演进

问题

试图一步到位完成所有服务的拆分,或拆分后就以为万事大吉,不需要进行适当的调整了。因为后续业务会有变化,团队规模会有变化,可能导致服务边界失效。

解决方法

  • 1、演进式拆分:从核心业务开始(如电商的订单、支付),逐步细化。或从非核心业务开始实践。
  • 2、绞杀者模式:在旧系统外围逐步构建新服务,逐步替换旧功能。
相关推荐
喜欢便码2 小时前
JS小练习0.1——弹出姓名
java·前端·javascript
王磊鑫3 小时前
重返JAVA之路-初识JAVA
java·开发语言
半兽先生3 小时前
WebRtc 视频流卡顿黑屏解决方案
java·前端·webrtc
南星沐4 小时前
Spring Boot 常用依赖介绍
java·前端·spring boot
代码不停5 小时前
Java中的异常
java·开发语言
拉不动的猪5 小时前
设计模式之--------工厂模式
前端·javascript·架构
何似在人间5755 小时前
多级缓存模型设计
java·jvm·redis·缓存
多云的夏天5 小时前
ubuntu24.04-MyEclipse的项目导入到 IDEA中
java·intellij-idea·myeclipse
Fanxt_Ja6 小时前
【数据结构】红黑树超详解 ---一篇通关红黑树原理(含源码解析+动态构建红黑树)
java·数据结构·算法·红黑树
Aphelios3806 小时前
TaskFlow开发日记 #1 - 原生JS实现智能Todo组件
java·开发语言·前端·javascript·ecmascript·todo