主题
微服务
服务拆分
注意
微服务的拆分没有标准,以下也只是给出指导性建议。
微服务拆分粒度的“三个火枪手”原则:一个微服务三个人负责开发。根据团队规模来划分微服务数量,如果业务和团队规模扩大,再将已有的微服务进行拆分。例如:团队初始有6人,那么可以划分为2个微服务,随着业务发展,业务功能越来越多,逻辑越来越复杂,团队扩展到12人,那么可以将已有的2个微服务进行拆分,变成4个微服务。
为什么是3个人?
- 从系统规模来讲,3个人复杂开发一个系统,系统的复杂度刚好达到每个人都能全面理解整个系统,又能够进行分工的粒度。如果是2个人,系统的复杂度不够,开发人员可能觉得无法体现自己的技术实力;如果是4个甚至更多人开发一个系统,系统复杂度又会无法让开发人员对系统的细节都了解很深。
- 从团队管理来说,3个人可以形成一个稳定的备份,即使1个人休假或者调配到其他系统,剩余2个人还可以支撑;如果是2个人,抽调1个后剩余的1个人压力很大;如果是1个人,这就是单点了,团队没有备份,某些情况下是很危险的,假如这个人休假了,系统出问题了怎么办?
- 从技术提升的角度来讲,3个人的技术小组既能够形成有效的讨论,又能够快速达成一致意见;如果是2个人,可能会出现互相坚持自己的意见,或者2个人经验都不足导致设计缺陷;如果是1个人,由于没有人跟他进行技术讨论,很可能陷入思维盲区导致重大问题;如果是4个人或者更多,可能有的参与的人员并没有认真参与,只是完成任务而已。
“三个火枪手”原则主要应用于微服务设计和开发阶段,如果微服务后续发展比较稳定,处于维护期,无须太多的开发,那么平均1个人维护1个微服务甚至几个微服务都可以。考虑人员备份的情况,每个人微服务最好有2个人维护。
基于业务逻辑拆分
这是最常见的一种拆分方式,将系统中的业务按职责范围识别出来,每个单独的业务模块拆分为一个独立的服务。但在实践中最常见的问题是,“职责范围”难以达成一致意见。例如:一个电商系统,可以将服务划分为“商品”、“交易”、“用户”3个服务,也可以划分为“商品”、“订单”、“支付”、“发货”、“买家”、“卖家”等6个服务。哪种方式更合理,是不是越细越好呢?
导致这种困惑的主要根因在于从业务的角度来拆分的话,规模粗和规模细都没有问题,因为拆分基础都是业务逻辑,要判断拆分粒度,不能从业务逻辑角度,而要根据前面介绍的“三个火枪手”的原则,计算一下大概的服务数量范围,然后再确定合适的“职责范围”,否则就可能出现划分过粗或者过细的情况,而且大部分情况下会出现过细的情况。
例如:如果团队规模是10个人,按照“三个火枪手”原则计算,大约需要划分为4个服务,那么“登录、注册、用户信息管理”都可以划到“用户服务”职责范围内;如果团队规模是100人,服务数量可以达到40个,那么“用户登录“就是一个服务了;如果团队规模达到1000人,那“用户连接管理”可能就是一个独立的服务了。
基于可扩展拆分
将系统中的业务模块按照稳定性排序,将已经成熟和改动不大的服务拆分为稳定服务,将经常变化和迭代的服务拆分为变动服务。稳定的服务粒度可以粗一些,即使逻辑上没有强关联的服务,也可以放在同一个子系统中,例如将“日志服务”和“升级服务”放在同一个子系统中;不稳定的服务粒度可以细一些,但也不要太细,始终记住要控制服务的总数量。
这样拆分主要是为了提升项目快速迭代的效率,避免在开发的时候,不小心影响了已有的成熟功能导致线上问题。
基于可靠性拆分
将系统中的业务模块按照优先级排序,将可靠性要求高的核心服务和可靠性要求低的非核心服务拆分开来,然后重点保证核心服务的高可用。具体拆分的时候,核心服务可以是一个也可以是多个,只要最终的服务数量满足“三个火枪手”原则就可以。
这样拆分带来的好处是:
- 避免非核心服务故障影响核心服务
- 核心服务高可用方案更加简单
- 能够降低高可用成本
基于性能拆分
基于性能拆分和基于可靠性拆分类似,将性能要求高或者性能压力大的模块拆分出来,避免性能压力大的服务影响其他服务。常见的拆分方式和具体的性能瓶颈有关,可以拆分Web服务、数据库、缓存等。例如电商的抢购,性能压力最大的是入口的排队功能,可以将排队功能独立为一个服务。
注意:以上的拆分方法不是多选一,而是可以根据实际情况自由排列组合,但要控制好最终的服务数量尽量满足“三个火枪手”原则。
基础设施
虽然建设完善的微服务基础设施是一项大工程,但如果微服务数量不多的话,并不是每个基础设施都是必须的。此外,现在已经有一些开源的微服务基础设施全家桶了,比如 Spring Cloud、Spring Clound Alibaba、Dubbo 等。通常情况下,可按照下面优先级来搭建基础设施:
- 服务发现、服务路由、服务容错:这是最基本的微服务基础设施。
- 接口框架、API网关:主要是为了提升开发消息。接口框架是提升内部服务的开发效率,API网关是为了提升与外部服务对接的效率。
- 自动化部署、自动化测试和配置中心:提升测试和运维效率。
- 服务监控、服务跟踪、服务安全:进一步提升运维效率。
以上3和4两类基础设施,其重要性会随着微服务节点数量增加而越来越重要,但在微服务节点数量较少的时候,可以通过人工的方式支撑,虽然效率不高,但也基本能够顶住。
服务发现
| 特性 | Nacos | Eureka | Consul | Zookeeper |
|---|---|---|---|---|
| 一致性协议 | CP + AP | AP | CP | CP |
| 健康检查 | TCP/HTTP/MySQL/ClientBeat | ClientBeat | TCP/HTTP/gRPC/CMD | KeepAlive |
| 负载均衡 | 权重/Metadata/Selector | Ribbon | Fabio | - |
| 雪崩保护 | 有 | 有 | 无 | 无 |
| 访问协议 | HTTP/DNS | HTTP | HTTP/DNS | TCP |
| 多数据中心 | 支持 | 支持 | 支持 | 不支持 |
| 跨注册中心同步 | 支持 | 不支持 | 支持 | 不支持 |
| Spring Cloud 集成 | 支持 | 支持 | 支持 | 支持 |
| Dubbo 集成 | 支持 | 不支持 | 支持 | 支持 |
| k8s 集成 | 支持 | 不支持 | 支持 | 不支持 |
Nacos
Eureka
Eureka 采用C/S架构,分为服务端和客户端两部分:
- Eureka Server:作为服务注册中心,负责管理所有可用的服务实例信息(即服务注册表),并提供服务的注册、发现和状态监控。
- Eureka Client:作为服务提供者或服务消费者,主要用于和 Eureka Server 进行交互。Client 默认每隔30秒会向 Server 发送心跳,若 Server 在多个心跳周期内(默认 90 秒)没有接收到某个 Client 的心跳,Server 将它从可用服务列表中移除。
注意
Eureka 2.0 已经停止维护。
Consul
- Client Agent:部署在每一个服务节点上。
- 服务注册:将本地服务的元数据转发给 Server。
- 健康检查:执行本地服务及其依赖(如端口、脚本)的健康检查。
- 转发查询:将服务发现或配置查询的 RPC 请求转发给 Server 集群。
- Server Agent:组成 Consul 的核心管理集群(通常3或5个节点)。它包含了 Client 的所有功能,并额外负责:
- 维护状态:通过 Raft 共识算法维护服务目录、配置信息的一致性与高可用。
- 响应RPC请求:直接处理客户端的服务发现和配置查询请求。
- 跨数据中心通信:通过 WAN Gossip 协议与其他数据中心的 Server 通信。
服务端代理(Server Agent)需要以单独的节点进行部署,不应与业务服务部署在同一节点上;而客户端代理(Client Agent)则必须与每一个业务服务实例部署在同一节点。
Zookeeper
ZooKeeper 本质上是一个分布式协调服务,其核心是一个基于 Paxos 算法变种(ZAB 协议)的、强一致性的分布式键值存储。它并非一个专门的服务发现工具,但可以利用其特性实现服务发现。
- Leader:集群的唯一主节点,负责处理所有写请求。它通过 ZAB 协议将数据变更同步到所有 Follower 和 Observer,保证数据的强一致性。
- Follower:参与投票选举 Leader 和参与写操作的一致性协议过程。它处理客户端的读请求,并将写请求转发给 Leader。
- Observer:与 Follower 类似,处理读请求并转发写请求,但不参与任何投票。它的存在纯粹是为了扩展集群的读性能,而不影响写操作的效率。
- ZNode:这是 ZooKeeper 数据模型的核心。它指的是数据树中的一个节点。ZNode 不仅可以存储少量数据(通常用于存储服务实例的元数据,如IP、端口),还可以通过创建临时节点(Ephemeral ZNode) 来实现服务发现和健康检查。
接口框架
微服务提倡轻量级的通信方式,一般采用HTTP/REST或者rpc方式统一接口协议,此外还要统一接口格式。比如使用HTTP/REST的数据要统一为如下的Json格式:
json
{
"requestId": 10001,
"time": "2025-09-14 11:20:00",
"caller": "app1",
"api": "getUser",
"params": {
"userId": 100
},
"sign": "xxx"
}API网关
系统拆分为微服务后,内部的微服务之间是互联互通的,相互之间是点对点访问。如果外部系统想要调用系统内部的某个功能接口,也采用点对点方式,那外部系统的开发人员将会崩溃。因为外部系统不需要也没办法理解这么多微服务的职责分工和边界,它只会关注它需要的功能,而不会关注这个功能应该由哪个微服务提供。
除此以外,外部系统访问系统还涉及安全和权限相关的限制,如果外部系统直接访问某个微服务,则意味着每个微服务都要自己实现安全和权限的功能,这样做不但工作量大,而且都是重复工作。
综合上面的分析,微服务需要一个统一的API网关,负责外部系统的访问操作。API网关是外部系统访问的接口,所有的外部系统接⼊系统都需要通过API网关,主要包括接入鉴权(是否允许接入)、权限控制(可以访问哪些功能)、传输加密、请求路由、流量控制等功能。