Skip to content

L1: 微服务架构认知 -- 什么时候该拆,怎么拆,拆多细

基于 Python 生态:Flask/FastAPI + gRPC + Consul + pybreaker + OpenTelemetry


一、为什么要微服务?单体架构到底有什么问题?

先说清楚:单体架构不是错,而是特定阶段的合理选择

单体架构的优势:

  • 开发简单:一个代码库,一个部署单元
  • 调试方便:本地跑起来就是全部
  • 事务简单:数据库本地事务就能解决
  • 运维简单:部署、监控、回滚都很直接
  • 性能优秀:没有网络调用开销

单体架构适合:

  • 业务初期,快速验证 MVP
  • 团队规模小(3~10 人)
  • 业务逻辑简单
  • 流量不大(单机扛得住)

举个例子:

一个创业团队做电商平台,3 个后端开发,用 Django/Flask 写一个单体应用:

ecommerce/
├── user/          # 用户模块
├── product/       # 商品模块
├── order/         # 订单模块
├── payment/       # 支付模块
└── manage.py      # 启动入口

这很合理,团队小、业务简单,单体足够。


但是,单体架构会在这些时候出问题:

1. 代码耦合,改一处影响全局

场景:

  • 商品服务要做秒杀改造
  • 因为和订单、库存逻辑耦合在一起
  • 改代码时不敢大改,怕影响订单
  • 测试时要测全链路
  • 上线时整个应用都要重新部署

后果:

  • 改动成本高,速度慢
  • 风险大,容易相互影响

2. 团队协作冲突

场景:

  • 用户团队和订单团队都在改同一个代码库
  • Git 冲突频繁
  • 代码评审相互等待
  • 一个团队发布阻塞另一个团队

后果:

  • 团队效率下降
  • 人多了反而慢

3. 技术栈绑定

场景:

  • 整个系统是 Django
  • 想引入机器学习服务(TensorFlow),但和 Django 集成麻烦
  • 想用 Go 写高性能网关,但单体架构做不到

后果:

  • 技术选型受限
  • 无法为不同场景选最优方案

4. 扩展性差

场景:

  • 商品浏览量大,需要多实例
  • 但整个单体应用都要扩展
  • 订单服务、支付服务也被迫扩展,浪费资源

后果:

  • 资源利用率低
  • 成本高

5. 故障影响面大

场景:

  • 某个模块(比如推荐服务)写了个慢 SQL
  • 拖垮整个应用的数据库连接池
  • 所有功能都不可用

后果:

  • 局部故障 → 全局故障
  • 可用性差

什么时候是拆分的信号?

信号说明
团队超过 10 人单体代码库协作效率急剧下降
发布周期拉长一个小改动也要整个应用发布,等待时间长
某些模块明显是性能瓶颈想单独扩展某个模块
不同模块想用不同技术栈比如订单用 Python,推荐用 Go
故障频繁相互影响一个模块挂了,整个系统挂
代码库超过 10 万行单体过于庞大,维护成本高

二、微服务是什么?核心特征是什么?

微服务的定义

微服务是一种架构风格,将一个单一应用拆分成一组小服务,每个服务:

  • 运行在独立进程中
  • 通过轻量级通信机制(通常是 HTTP REST 或 gRPC)相互协作
  • 可以独立部署、独立扩展、独立演进
  • 由独立的小团队负责

微服务的核心特征(必须记住)

1. 服务自治

  • 每个服务独立开发、独立部署、独立运维
  • 服务有自己的数据库(或独立 Schema)
  • 服务可以用不同技术栈

2. 按业务能力拆分

  • 不是按技术层拆分(MVC),而是按业务领域拆分
  • 比如:用户服务、订单服务、支付服务

3. 去中心化治理

  • 没有统一的技术栈要求
  • 每个服务团队自己选型
  • 数据去中心化:每个服务管理自己的数据

4. 基础设施自动化

  • 强依赖 CI/CD
  • 强依赖容器化(Docker/K8s)
  • 强依赖自动化测试

5. 为失败而设计

  • 默认服务会失败
  • 要有熔断、降级、重试、超时机制
  • 要有监控、告警、链路追踪

三、微服务不是银弹,代价是什么?

微服务的代价:

1. 分布式复杂性

  • 网络延迟
  • 网络不可靠
  • 分布式事务难
  • 数据一致性难

2. 运维复杂度暴增

  • 单体 1 个部署单元 → 微服务可能 20 个
  • 监控、日志、链路追踪必须有
  • 没有自动化运维会累死

3. 服务间调用开销

  • 本地方法调用变成网络调用
  • 性能有损耗
  • 要做序列化/反序列化

4. 数据一致性难

  • 不能用数据库本地事务
  • 要引入分布式事务、最终一致性
  • 业务逻辑变复杂

5. 测试复杂

  • 集成测试要启动多个服务
  • 端到端测试成本高
  • 环境管理复杂

6. 团队能力要求高

  • 需要懂分布式
  • 需要懂容器化
  • 需要懂服务治理
  • 小团队可能 hold 不住

所以,微服务的前提条件:

前提条件说明
团队规模至少 10+ 人
业务复杂度业务足够复杂,值得拆分
自动化能力有 CI/CD、容器化能力
监控能力有日志、监控、链路追踪基础设施
分布式系统经验团队有分布式系统开发经验
业务稳定性要求高愿意投入成本做高可用

如果以上条件不满足,不要盲目上微服务。


四、服务怎么拆?拆分的 5 个核心原则

原则 1:按业务能力拆分,不按技术层拆分

错误示范(按技术层拆分):

- user-controller-service
- user-service-service
- user-dao-service

这不是微服务,这是"分布式单体"。

正确示范(按业务能力拆分):

- user-service         # 用户服务:注册、登录、信息管理
- product-service      # 商品服务:商品 CRUD、库存查询
- order-service        # 订单服务:下单、订单查询、订单状态
- payment-service      # 支付服务:支付、退款
- notification-service # 通知服务:发邮件、发短信

每个服务是一个完整的业务能力。


原则 2:高内聚,低耦合

高内聚:

  • 一个服务内部的功能高度相关
  • 比如订单服务:下单、取消订单、查询订单,都和"订单"相关

低耦合:

  • 服务之间依赖尽量少
  • 服务之间通过 API 通信,不直接访问对方数据库

判断标准: 如果两个功能经常一起变,应该在一个服务里。 如果两个功能可以独立演进,应该在不同服务里。


原则 3:服务要有明确的业务边界

用 DDD(领域驱动设计)的概念:

  • 每个服务对应一个 限界上下文(Bounded Context)
  • 限界上下文内的概念和术语统一
  • 不同上下文的概念可以不同

举例:

订单服务 里,"商品"可能只是:

python
class OrderItem:
    product_id: str
    product_name: str  # 快照
    price: Decimal     # 快照

商品服务 里,"商品"是完整的:

python
class Product:
    id: str
    name: str
    price: Decimal
    stock: int
    category: str
    description: str
    images: List[str]

两个服务里的"商品"概念不同,这是合理的。


原则 4:一个服务一个数据库(或独立 Schema)

为什么?

  • 数据隔离,服务自治
  • 避免数据库层耦合
  • 方便独立扩展

错误做法:

user-service  ──┐
order-service ──┼──> 同一个数据库
product-service ─┘

这样服务间会通过数据库相互影响,本质上还是单体。

正确做法:

user-service    → user_db
order-service   → order_db
product-service → product_db

每个服务有自己的数据库。

但注意: 这会带来数据一致性问题,需要分布式事务或最终一致性方案。


原则 5:服务粒度要适中

过粗:

  • 一个"业务服务"包含用户、商品、订单
  • 本质上还是单体

过细:

  • 一个服务只有一个接口
  • 运维成本暴增
  • 调用链路过长

合适的粒度:

  • 一个服务对应一个业务领域
  • 一个团队(或半个团队)能维护
  • 服务数量控制在 10~30 个(中小团队)

判断标准:

  • 如果两个服务离了对方都没法工作 → 应该合并
  • 如果一个服务太大,一个团队维护吃力 → 应该拆分

五、实战案例:电商系统怎么拆微服务?

需求分析

一个典型电商系统包含:

  • 用户注册、登录
  • 商品浏览、搜索
  • 购物车
  • 下单、支付
  • 订单查询
  • 库存管理
  • 通知(邮件、短信)
  • 推荐系统

拆分方案(基于业务能力)

┌─────────────────────────────────────────────────┐
│                  API Gateway                    │  # 统一入口
└─────────────────────────────────────────────────┘

           ├──> user-service          # 用户服务
           ├──> product-service       # 商品服务
           ├──> cart-service          # 购物车服务
           ├──> order-service         # 订单服务
           ├──> payment-service       # 支付服务
           ├──> inventory-service     # 库存服务
           ├──> notification-service  # 通知服务
           └──> recommendation-service # 推荐服务

各服务职责

1. user-service(用户服务)

  • 用户注册、登录、登出
  • 用户信息管理
  • 用户鉴权(JWT 签发)

数据库:

user_db:
  - users 表
  - user_profiles 表

2. product-service(商品服务)

  • 商品 CRUD
  • 商品分类
  • 商品搜索(可能调用 Elasticsearch)
  • 商品详情查询

数据库:

product_db:
  - products 表
  - categories 表

3. cart-service(购物车服务)

  • 加入购物车
  • 购物车查询
  • 删除购物车

数据存储:

  • Redis(购物车通常不持久化)

4. order-service(订单服务)

  • 下单
  • 订单查询
  • 取消订单
  • 订单状态变更

数据库:

order_db:
  - orders 表
  - order_items 表(订单明细)

冗余字段(快照):

  • 商品名称快照
  • 商品价格快照
  • 收货地址快照

为什么冗余? 因为下单后,商品价格可能改变,但历史订单价格不能变。


5. payment-service(支付服务)

  • 发起支付
  • 支付回调
  • 退款

数据库:

payment_db:
  - payments 表

对外调用:

  • 第三方支付(微信支付、支付宝)

6. inventory-service(库存服务)

  • 库存查询
  • 库存扣减
  • 库存回滚

数据库:

inventory_db:
  - inventory 表

注意: 库存扣减是高并发场景,要做好并发控制。


7. notification-service(通知服务)

  • 发送邮件
  • 发送短信
  • 发送站内信

数据库: 可选,通常只记录发送日志。

消息队列: 通常由其他服务发消息到 MQ,通知服务消费。


8. recommendation-service(推荐服务)

  • 个性化推荐
  • 热门商品推荐

技术栈: 可能用 Python(机器学习)+ TensorFlow

数据来源:

  • 读取商品服务数据
  • 读取用户行为数据

服务间调用关系

用户下单流程:
user-service(鉴权)

order-service(创建订单)

├──> product-service(查询商品价格、信息)
├──> inventory-service(扣减库存)
└──> payment-service(发起支付)

notification-service(发送通知)

数据一致性问题

问题: 下单时需要:

  1. 创建订单
  2. 扣减库存
  3. 发起支付

如果 2 或 3 失败了怎么办?

解决方案(后续章节详细讲):

  • 本地消息表 + 最终一致性
  • Saga 分布式事务
  • 补偿机制

六、拆分的常见坑点(必须规避)

坑点 1:过度拆分

案例: 一个 3 人团队,拆了 15 个微服务。

问题:

  • 运维成本暴增
  • 一个需求改 5 个服务
  • 联调成本比开发成本还高

教训: 服务数量要和团队规模匹配。


坑点 2:共享数据库

案例: 多个服务直连同一个数据库。

问题:

  • 表结构耦合
  • 数据库成为瓶颈
  • 本质上还是"分布式单体"

教训: 每个服务必须有独立数据库(或独立 Schema)。


坑点 3:同步调用链路过长

案例:

A → B → C → D → E

问题:

  • 任何一个环节慢,整条链路都慢
  • 延迟叠加
  • 可用性差

教训:

  • 同步链路不超过 3~4 层
  • 能异步的用消息队列

坑点 4:服务划分不清

案例: 订单服务和库存服务相互调用,循环依赖。

问题:

  • 服务边界不清
  • 维护困难

教训: 服务间应该是单向依赖,不应该循环依赖。


坑点 5:没有 API 网关

案例: 前端直接调多个微服务。

问题:

  • 跨域问题
  • 鉴权重复
  • 前端要知道所有服务地址

教训: 必须有 API 网关作为统一入口。


七、面试高频题

1. 什么是微服务?和单体架构有什么区别?

参考答案:

微服务是一种架构风格,将单一应用拆分成一组小服务,每个服务独立部署、独立扩展、独立演进。

和单体的区别:

维度单体架构微服务架构
部署单元一个多个
技术栈统一可不同
数据库共享独立
团队协作集中式去中心化
扩展性整体扩展按服务扩展
故障隔离局部故障全局影响故障隔离
复杂度高(分布式复杂性)

2. 什么时候该从单体拆微服务?

参考答案:

以下信号说明该考虑微服务:

  1. 团队超过 10 人,单体协作效率低
  2. 发布周期拉长,小改动也要整体发布
  3. 某些模块是性能瓶颈,想单独扩展
  4. 不同模块想用不同技术栈
  5. 故障频繁相互影响

但前提是:

  • 有自动化能力(CI/CD、容器化)
  • 有监控、日志、链路追踪
  • 团队有分布式系统经验

如果条件不满足,不要盲目拆。


3. 微服务怎么拆分?有什么原则?

参考答案:

核心原则:

  1. 按业务能力拆分,不按技术层拆分
  2. 高内聚,低耦合
  3. 服务有明确的业务边界(DDD 限界上下文)
  4. 一个服务一个数据库
  5. 服务粒度适中(不过粗不过细)

判断标准:

  • 经常一起变的放一个服务
  • 可以独立演进的拆开
  • 服务数量和团队规模匹配

4. 什么是"分布式单体"?怎么避免?

参考答案:

分布式单体: 看起来拆成了多个服务,但本质上耦合严重,改一处影响全局。

典型特征:

  • 多个服务共享数据库
  • 服务间循环依赖
  • 一个需求要改多个服务,且必须同时发布
  • 服务按技术层拆分,不按业务拆分

怎么避免:

  1. 每个服务独立数据库
  2. 服务间单向依赖,不循环依赖
  3. 按业务能力拆分
  4. 服务能独立部署

5. 微服务的优缺点?

参考答案:

优点:

  • 服务独立部署、独立扩展
  • 技术栈灵活
  • 故障隔离
  • 团队自治

缺点:

  • 分布式复杂性(网络、一致性、事务)
  • 运维成本高
  • 测试复杂
  • 服务间调用有开销
  • 团队能力要求高

结论: 微服务不是银弹,要根据团队规模、业务复杂度、技术能力决定。


八、核心结论

  1. 单体不是错,微服务不是万能,要根据团队和业务选择
  2. 拆分的信号:团队大、发布慢、模块瓶颈、技术栈限制
  3. 拆分原则:按业务能力、高内聚低耦合、服务有边界、独立数据库
  4. 微服务代价:分布式复杂性、运维成本、数据一致性
  5. 避免分布式单体:独立数据库、单向依赖、按业务拆分
  6. 电商系统典型拆分:用户、商品、订单、支付、库存、通知
  7. 服务粒度要适中,和团队规模匹配
  8. 必须有 API 网关、注册中心、监控、链路追踪

九、练习题

练习 1:服务拆分设计

假设你要设计一个 在线教育平台,包含:

  • 用户注册、登录
  • 课程浏览、购买
  • 视频播放
  • 学习进度记录
  • 作业提交、批改
  • 讨论区
  • 推荐课程

请设计:

  1. 如何拆分服务?
  2. 每个服务的职责是什么?
  3. 每个服务的数据库表有哪些?
  4. 服务间的调用关系?

练习 2:判断是否该拆微服务

场景 1: 一个 5 人团队,做一个内部管理系统,日活 100 人。

是否该拆微服务?为什么?


场景 2: 一个 50 人团队,做一个电商平台,日活 10 万,单体应用代码 20 万行,发布周期从 1 周拉长到 1 个月。

是否该拆微服务?为什么?


练习 3:思考题

为什么微服务要"一个服务一个数据库",而不能多个服务共享一个数据库?

列出至少 3 个理由。

基于 VitePress 构建