微服务是现在最流行的架构风格之一。

最近几年,微服务越来越火很多公司都在从单体架构迁移到微服务架构。Spring CloudDubboKubernetes等微服务相关的技术也越来越成熟。

但是微服务拆分是一门艺术也是一门科学。拆得好系统灵活可扩展可维护团队协作高效。拆得不好系统混乱服务间耦合严重分布式事务复杂运维困难反而不如单体架构。

很多人在做微服务拆分的时候,都是凭感觉拆,或者按照技术层拆拆完才发现各种问题。

今天想深入剖析微服务拆分的原理和底层机制帮大家理解微服务拆分的本质做出合理的拆分决策。

一、微服务的本质

在讲拆分之前,先理解微服务的本质。

微服务的本质是什么?

很多人以为微服务就是把一个大的应用拆成很多小的服务每个服务独立部署独立运行。这是微服务的表现形式,但是不是本质。

微服务的本质是松耦合高内聚的服务化架构。

松耦合是指服务之间,依赖少一个服务的变化不会影响其他服务。高内聚是指一个服务内部的功能紧密相关都是同一个业务领域的功能。

微服务的目标是让系统更灵活更可扩展更可维护团队能独立开发独立部署独立扩展提高开发效率和,系统的可靠性。

微服务不是目的而是手段。如果单体架构能满足需求就不需要微服务。微服务带来了灵活性,但是也带来了复杂度分布式系统的各种问题都需要解决。

所以在做微服务拆分之前,要想清楚为什么要做微服务要解决什么问题不要为了微服务而微服务。

二、微服务拆分的原则

微服务拆分有一些基本原则遵循这些原则能避免很多坑。

原则1:单一职责原则

单一职责原则是面向对象设计的基本原则也适用于微服务拆分。

一个微服务应该只负责一个业务领域的功能只做一件事,并且把这件事做好。

如果一个服务负责了多个不相关的业务领域的功能那这个服务的内聚性就低耦合性就高不好维护也不好扩展。

比如一个电商系统用户管理商品管理订单管理支付管理这些是,不同的业务领域应该拆成不同的服务而不是放一个服务里。

原则2:高内聚低耦合

高内聚低耦合是软件设计的总原则也是微服务拆分的核心原则。

高内聚是指一个服务内部的功能紧密相关都是同一个业务领域的功能。低耦合是指服务之间,依赖少一个服务的变化不会影响其他服务。

拆分的时候,要让相关的功能放一起不相关的功能分开。服务之间,通过接口通信不要直接操作对方的数据库不要共享代码不要共享数据。

原则3:服务自治原则

服务自治是指每个微服务都应该独立开发独立部署独立运行独立扩展独立数据存储。

一个服务的开发测试部署运行都不应该依赖其他服务。服务之间,通过网络通信松耦合。

服务自治的一个重要方面是数据自治。每个服务应该有自己的数据库,或者自己的数据表不要和其他服务共享数据库。共享数据库会导致服务之间,耦合严重一个服务修改表结构会影响其他服务。

原则4:限界上下文原则

限界上下文是领域驱动设计(DDD)的概念也是微服务拆分的重要依据。

限界上下文是指一个业务领域的边界在这个边界内领域模型是统一的一致的。不同的限界上下文之间,领域模型可能不同同一个概念在不同的限界上下文中可能有不同的含义。

微服务拆分应该按照限界上下文来拆一个限界上下文对应一个微服务。这样服务的边界清晰内聚性高。

比如在电商系统中"商品"这个概念在商品管理上下文中包含商品的基本信息分类属性等等。在订单上下文中"商品"可能只包含商品ID名称价格等下单需要的信息。这两个上下文的"商品"模型不同应该拆成不同的服务。

原则5:演进式拆分原则

微服务拆分不是一蹴而就的应该演进式拆分。

不要一开始就把系统拆成很多微服务那样风险大容易出问题。应该先从单体开始,或者从粗粒度的服务开始,然后根据业务发展和需求逐步拆分细化。

演进式拆分能降低风险让团队逐步适应微服务的开发方式也能避免过度拆分。

而且业务是不断变化的今天合理的拆分明天可能就不合理了。所以拆分应该是持续的演进的根据业务变化调整。

三、微服务拆分的方法

讲完了原则再讲讲拆分的方法。

方法1:按业务领域拆分

按业务领域拆分是最常用的拆分方法也是最推荐的方法。

按照业务领域把系统拆成不同的服务每个服务负责一个业务领域的功能。

比如电商系统可以拆成:

  • 用户服务:负责用户注册登录信息管理
  • 商品服务:负责商品信息分类库存
  • 订单服务:负责订单创建查询状态管理
  • 支付服务:负责支付退款对账
  • 物流服务:负责物流信息配送跟踪
  • 营销服务:负责优惠券活动促销

按业务领域拆分的好处是服务边界清晰内聚性高团队能按业务领域划分独立负责。

方法2:按子域拆分

按子域拆分是领域驱动设计(DDD)的方法。

在DDD中一个业务领域可以分成多个子域核心域支撑域通用域。每个子域可以对应一个微服务。

核心域是业务的核心是公司的核心竞争力应该重点投入拆成独立的服务。支撑域是支撑核心域的功能不是核心,但是必要。通用域是通用的功能很多系统都需要可以用第三方服务,或者通用组件。

按子域拆分能让团队聚焦核心域把资源投入到最有价值的地方。

方法3:按限界上下文拆分

按限界上下文拆分也是DDD的方法和按子域拆分类似,但是更细。

一个子域可能包含多个限界上下文每个限界上下文对应一个微服务。

限界上下文的划分需要深入理解业务领域识别领域模型的边界。这需要和领域专家沟通事件风暴等方法来识别。

方法4:按变更频率拆分

按变更频率拆分是根据功能的变更频率来拆分。

变更频繁的功能和变更不频繁的功能应该分开。因为变更频繁的功能需要经常部署和变更不频繁的功能放一起会影响稳定性。

比如电商系统中营销活动的功能变更很频繁经常要上新活动改规则。而用户基本信息的功能变更很少。这两个应该分开拆成不同的服务。

方法5:按性能需求拆分

按性能需求拆分是根据功能的性能需求来拆分。

性能需求高的功能和性能需求低的功能应该分开。因为性能需求高的功能需要独立扩展优化和其他功能放一起会互相影响。

比如电商系统中商品搜索的功能性能需求很高需要快速响应高并发。而商品评价的功能性能需求相对低。这两个应该分开拆成不同的服务搜索服务可以独立扩展优化。

方法6:按团队结构拆分

按团队结构拆分是根据团队的组织结构来拆分。

康威定律说系统的架构会反映组织的沟通结构。所以拆分服务的时候,要考虑团队的结构让每个团队能独立负责一个或多个服务。

如果一个服务需要多个团队共同维护那这个服务的边界可能不合理应该拆分。

按团队结构拆分能让团队自治提高开发效率。

四、服务边界的划分

服务边界的划分是微服务拆分的核心也是最难的部分。

边界划得好服务松耦合高内聚好维护。边界划得不好服务耦合严重分布式事务复杂不好维护。

怎么划分服务边界?

1. 识别领域模型

首先,要识别业务领域的领域模型理解业务的核心概念和关系。

可以用事件风暴等方法和领域专家一起梳理业务流程识别领域事件命令聚合实体值对象等领域模型。

2. 识别限界上下文

然后根据领域模型识别限界上下文。

限界上下文是领域模型的边界在边界内领域模型是统一的。不同的限界上下文之间,领域模型可能不同。

识别限界上下文的一个方法是看同一个概念在不同的业务场景中是否有不同的含义和,属性。如果有说明它们属于不同的限界上下文。

3. 定义服务接口

划分了限界上下文之后,每个限界上下文对应一个服务。然后定义服务之间,的接口。

服务接口应该粗粒度不要太细。太细的接口会导致服务之间,调用频繁性能差耦合高。

服务接口应该稳定不要经常变。接口变了会影响所有调用方。

服务之间,通信应该通过接口不要直接操作对方的数据库不要共享代码。

4. 处理跨服务的业务流程

很多业务流程会跨多个服务,比如下单流程涉及订单服务商品服务支付服务物流服务等等。

跨服务的业务流程怎么处理?

常用的方法有:

  • 同步调用:服务之间,同步调用,比如RESTgRPC。简单,但是耦合高一个服务挂了整个流程都受影响。
  • 异步消息:服务之间,通过消息队列异步通信,比如KafkaRabbitMQ。松耦合可靠,但是复杂度高需要处理消息一致性。
  • Saga模式:用Saga模式处理跨服务的分布式事务每个服务有,自己的本地事务通过补偿操作保证最终一致性。

跨服务的业务流程要尽量用异步消息和最终一致性不要用强一致性的分布式事务,因为强一致性的分布式事务性能差复杂度高。

五、数据的拆分

数据拆分是微服务拆分的重要部分也是很多人容易忽略的部分。

很多人拆了服务,但是数据还是共享一个数据库这样服务之间,还是耦合严重不算真正的微服务。

1. 每个服务独立数据库

微服务的一个重要原则是数据自治每个服务应该有自己的数据库,或者自己的Schema不要和其他服务共享。

共享数据库会导致:

  • 服务之间,耦合严重一个服务修改表结构会影响其他服务。
  • 数据安全问题一个服务可以操作其他服务的数据。
  • 性能问题多个服务访问同一个数据库互相影响。
  • 难以独立扩展数据库不能按服务独立扩展。

所以每个服务应该有自己的数据库服务之间,通过接口交换数据不要直接访问对方的数据库。

2. 数据冗余和同步

每个服务独立数据库之后,会有数据冗余的问题。比如订单服务需要商品的名称和价格,但是商品数据在商品服务的数据库里。

怎么处理?

常用的方法有:

  • 服务间调用:订单服务创建订单时调用商品服务获取商品信息。简单,但是耦合高性能差。
  • 数据冗余:订单服务的数据库里冗余一份商品的基本信息创建订单时直接用本地数据。性能好,但是数据一致性需要处理商品信息变了要同步到订单服务。
  • 事件驱动:商品服务商品信息变化时发事件到消息队列订单服务订阅事件更新本地冗余数据。松耦合可靠,但是复杂度高。

一般推荐用数据冗余+事件驱动的方式性能好松耦合。

3. 分布式事务

微服务架构下跨服务的事务是一个难题。

传统的单体架构用本地事务就能保证一致性。微服务架构下一个业务操作可能跨多个服务每个服务有自己的数据库本地事务只能保证单个服务的一致性跨服务的一致性需要分布式事务。

分布式事务的方案有:

  • 两阶段提交(2PC):强一致性,但是性能差阻塞严重不推荐。
  • TCC(Try-Confirm-Cancel):补偿型事务性能较好,但是侵入性强每个服务要实现TryConfirmCancel三个接口。
  • Saga模式:事件驱动的补偿型事务每个服务有自己的本地事务通过事件触发下一个服务的操作,如果某个步骤失败触发补偿操作回滚。松耦合性能好,但是是最终一致性不是强一致性。
  • 本地消息表:用本地消息表+消息队列实现最终一致性简单可靠。

微服务架构下推荐用最终一致性不要追求强一致性,因为强一致性的分布式事务性能差复杂度高不符合微服务的松耦合原则。

六、服务间通信

服务间通信是微服务架构的重要部分。

微服务之间,需要通信协作完成业务功能。服务间通信的方式有很多选择合适的通信方式很重要。

1. 同步通信

同步通信是指服务调用其他服务后等待返回结果才继续执行。

常用的同步通信方式有:

  • REST:基于HTTP的RESTful API简单通用易理解,但是性能一般耦合较高。
  • gRPC:基于HTTP/2的RPC框架性能好支持多种语言,但是需要定义proto文件学习成本稍高。

同步通信的优点是简单易理解调用结果立即可知。缺点是耦合高一个服务挂了调用方也受影响,而且服务之间,调用链长了性能差可靠性低。

2. 异步通信

异步通信是指服务调用其他服务后不等待返回结果继续执行结果通过回调,或者事件通知。

常用的异步通信方式有:

  • 消息队列:通过消息队列异步通信,比如KafkaRabbitMQRocketMQ。松耦合可靠削峰填谷,但是复杂度高需要处理消息一致性。
  • 事件驱动:服务发布事件其他服务订阅事件做出响应。松耦合可扩展,但是调试困难流程不直观。

异步通信的优点是松耦合可靠性能好削峰填谷。缺点是复杂度高调试困难一致性需要处理。

3. 通信方式的选择

怎么选择通信方式?

  • 如果需要立即获取结果,而且调用链短用同步通信,比如查询操作。
  • 如果不需要立即获取结果,或者调用链长用异步通信,比如下单流程通知操作。
  • 如果需要解耦和削峰填谷用消息队列。
  • 如果需要实时性高性能好用gRPC。

一般推荐同步和异步结合用查询用同步命令和事件用异步。

七、微服务拆分的常见误区

最后讲讲微服务拆分的常见误区。

误区1:按技术层拆分

很多人拆分微服务的时候,按照技术层拆,比如数据访问层业务逻辑层接口层拆成不同的服务。

这是错误的。按技术层拆分服务之间,耦合严重一个业务操作需要调用多个服务性能差,而且服务的内聚性低。

微服务应该按业务领域拆分不是按技术层拆分。

误区2:拆分过细

很多人以为微服务越小越好把系统拆成很多很小的服务甚至一个接口一个服务。

这是错误的。拆分过细会导致服务数量太多运维复杂服务之间,调用频繁性能差分布式事务复杂反而不如单体。

微服务的粒度要适中一个服务应该负责一个完整的业务领域的功能不是越小越好。

误区3:共享数据库

很多人拆了服务,但是数据还是共享一个数据库服务之间,直接操作对方的表。

这是错误的。共享数据库会导致服务之间,耦合严重不算真正的微服务。

每个服务应该有自己的数据库服务之间,通过接口交换数据。

误区4:追求强一致性

很多人在微服务架构下还是追求强一致性用分布式事务保证跨服务的强一致性。

这是不推荐的。强一致性的分布式事务性能差复杂度高不符合微服务的松耦合原则。

微服务架构下应该用最终一致性接受短暂的不一致通过补偿和重试保证最终一致。

误区5:一开始就全量拆分

很多人做微服务的时候,一开始就把整个系统全量拆成微服务。

这是风险很大的。全量拆分周期长风险大容易出问题,而且团队需要时间适应微服务的开发方式。

应该演进式拆分先拆最需要的部分逐步拆分细化降低风险。

八、写在最后

以上就是微服务拆分原理的深入剖析。

微服务拆分是一门艺术也是一门科学。它需要深入理解业务领域遵循拆分原则选择合适的拆分方法划分清晰的服务边界处理好数据拆分和,服务间通信。

拆得好微服务能给系统带来灵活性可扩展性可维护性提高团队的开发效率。拆得不好微服务会带来复杂度和各种问题反而不如单体。

所以在做微服务拆分之前,要想清楚为什么要做微服务要解决什么问题不要为了微服务而微服务。拆分的时候,要遵循原则用合适的方法演进式拆分不要一步到位。

希望这篇文章能帮大家深入理解微服务拆分的底层机制做出合理的拆分决策。

如果有什么问题,或者不同的看法欢迎在评论区留言我们一起交流。

最后用一句话结束这篇文章:"微服务不是目的而是手段合适的才是最好的。"

愿大家都能做好微服务拆分构建灵活可扩展的系统。