HiHuo
首页
博客
手册
工具
关于
首页
博客
手册
工具
关于
  • 微服务架构实战

    • 微服务架构设计手册
    • 第1章:微服务架构概述
    • 第2章:服务拆分与边界
    • 第3章:服务间通信
    • 第4章:数据一致性方案

第2章:服务拆分与边界

服务拆分原则

1. 单一职责原则(Single Responsibility Principle)

定义:一个服务应该只有一个引起它变化的原因

错误示例:

 UserService包含:
   - 用户注册/登录
   - 用户资料管理
   - 用户积分管理
   - 用户消息通知

问题:职责过多,变化原因太多

正确示例:

 UserService: 用户基本信息(注册、登录、资料)
 PointService: 积分管理
 NotificationService: 消息通知

每个服务职责单一明确

2. 高内聚低耦合

高内聚:相关功能聚集在一起

 订单服务包含:
   - 创建订单
   - 修改订单
   - 取消订单
   - 查询订单

这些都是订单相关的操作,应该在一起

低耦合:服务间依赖最小化

 订单服务 → 直接访问用户数据库
 订单服务 → 调用用户服务API

通过API通信,而非直接访问数据库

3. 业务能力对齐

按业务能力拆分,而非技术层次

错误拆分(按技术层):

 ControllerService: 所有Controller
 ServiceLayerService: 所有业务逻辑
 DAOService: 所有数据访问

问题:
- 跨服务调用频繁
- 业务变更涉及多个服务
- 违反高内聚原则

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

 UserService: 用户管理
   - UserController
   - UserService
   - UserRepository

 OrderService: 订单管理
   - OrderController
   - OrderService
   - OrderRepository

优势:
- 业务内聚
- 团队自治
- 变更影响范围小

4. 领域驱动设计(DDD)

按限界上下文拆分

电商系统的限界上下文:

┌──────────────────┐
│  用户上下文       │
│  User: 认证信息   │
└──────────────────┘

┌──────────────────┐
│  订单上下文       │
│  User: 收货信息   │
│  Order: 订单详情  │
└──────────────────┘

┌──────────────────┐
│  营销上下文       │
│  User: 标签、画像  │
└──────────────────┘

同一个"User"在不同上下文中含义不同!

边界识别方法

方法1:业务流程分析

步骤:

  1. 绘制业务流程图
用户注册流程:
1. 填写注册信息
2. 验证手机号
3. 创建账户
4. 发送欢迎邮件

可拆分为:
- 用户服务:创建账户
- 短信服务:验证手机号
- 邮件服务:发送邮件
  1. 识别业务边界
下单流程:
1. 选择商品 → 商品服务
2. 确认订单 → 订单服务
3. 扣减库存 → 库存服务
4. 创建支付 → 支付服务

自然边界:商品、订单、库存、支付

方法2:数据边界分析

原则:独立数据库 = 服务边界

-- 用户数据
users表:
  id, name, email, phone

-- 订单数据
orders表:
  id, user_id, total, status

order_items表:
  id, order_id, product_id, quantity

规则:
- users → User Service
- orders + order_items → Order Service
- 通过user_id关联,而非JOIN

方法3:团队组织结构

康威定律:系统架构反映组织沟通结构

组织结构:
- 用户团队(5人)
- 订单团队(8人)
- 商品团队(6人)
- 支付团队(4人)

服务拆分应对齐团队:
- User Service → 用户团队
- Order Service → 订单团队
- Product Service → 商品团队
- Payment Service → 支付团队

方法4:变化频率分析

高变化vs低变化

高变化频率:
- 营销活动(每周变化)→ MarketingService
- 推荐算法(频繁调整)→ RecommendationService

低变化频率:
- 用户认证(稳定)→ AuthService
- 基础商品信息(稳定)→ ProductService

策略:
高变化的功能独立成服务,便于快速迭代

拆分粒度控制

粒度权衡

过大的服务(单体趋势):

 ECommerceService包含:
   - 用户管理
   - 商品管理
   - 订单管理
   - 支付管理
   - 物流管理

问题:
- 失去微服务优势
- 团队耦合
- 难以独立扩展

过小的服务(纳米服务):

 CreateOrderService: 只负责创建订单
 UpdateOrderService: 只负责更新订单
 CancelOrderService: 只负责取消订单

问题:
- 服务数量爆炸
- 网络开销大
- 分布式事务复杂
- 运维成本高

合理粒度:

 OrderService包含:
   - 创建订单
   - 更新订单
   - 取消订单
   - 查询订单

黄金法则:
- 2-pizza团队可维护(5-10人)
- 代码量:几千到几万行
- 部署时间:< 15分钟

拆分粒度指标

量化指标:

维度推荐值说明
团队规模2-8人2-pizza原则
代码行数5k-50k过大则拆分
API数量5-20个过多则职责不清
数据表数量3-15个独立数据库
部署时间< 15分钟快速部署
依赖服务数< 5个避免过度耦合

常见拆分陷阱

陷阱1:按技术分层拆分

错误示例:

 ControllerService
 BusinessLogicService
 DatabaseService

问题:
- 违反业务内聚
- 跨服务调用频繁
- 网络开销大

正确做法:

 UserService (包含Controller + Service + Repository)
 OrderService (完整业务能力)

陷阱2:过度拆分

案例:

某公司将用户服务拆分为:
- UserRegistrationService
- UserLoginService
- UserProfileService
- UserPasswordService
- UserAvatarService
... 共20个服务

结果:
 服务数量失控
 分布式事务复杂
 运维成本暴增

解决:

合并为UserService
包含所有用户相关功能

陷阱3:循环依赖

错误设计:

OrderService → UserService (获取用户信息)
UserService → OrderService (获取订单数量)

问题:
- 循环依赖
- 部署困难
- 故障传播

解决方案:

方案1:引入中间服务

OrderService → UserProfileService
UserService → UserProfileService

UserProfileService聚合用户+订单信息

方案2:事件驱动

OrderService: 创建订单 → 发布OrderCreatedEvent
UserService: 监听OrderCreatedEvent → 更新订单数量

异步解耦,消除循环依赖

陷阱4:共享数据库

错误做法:

OrderService ─┐
ProductService├→ Shared Database
UserService  ─┘

问题:
- 紧耦合
- schema变更影响多个服务
- 无法独立扩展数据库

正确做法:

OrderService → Order DB
ProductService → Product DB
UserService → User DB

每个服务独立数据库

实战案例

电商系统服务拆分

业务分析:

核心业务流程:
1. 用户浏览商品
2. 加入购物车
3. 创建订单
4. 支付
5. 发货
6. 确认收货

初步拆分:

核心域(Core Domain):
 Product Service: 商品管理
 Order Service: 订单管理
 Payment Service: 支付管理

支撑域(Supporting Domain):
 User Service: 用户管理
 Inventory Service: 库存管理
 Shipping Service: 物流管理

通用域(Generic Domain):
 Notification Service: 通知服务
 Search Service: 搜索服务

服务详细设计:

1. Product Service(商品服务)

// API接口
type ProductService interface {
    // 商品管理
    CreateProduct(product *Product) error
    UpdateProduct(id string, product *Product) error
    DeleteProduct(id string) error

    // 商品查询
    GetProduct(id string) (*Product, error)
    ListProducts(page, pageSize int) ([]*Product, error)
    SearchProducts(keyword string) ([]*Product, error)
}

// 数据模型
type Product struct {
    ID          string
    Name        string
    Description string
    Price       decimal.Decimal
    CategoryID  string
    Stock       int
    Images      []string
    Status      ProductStatus
    CreatedAt   time.Time
    UpdatedAt   time.Time
}

// 数据库表
products:
  id, name, description, price, category_id, stock, status, created_at, updated_at

product_images:
  id, product_id, image_url, sort_order

categories:
  id, name, parent_id, level

2. Order Service(订单服务)

// API接口
type OrderService interface {
    // 订单操作
    CreateOrder(req CreateOrderRequest) (*Order, error)
    CancelOrder(orderID string) error

    // 订单查询
    GetOrder(orderID string) (*Order, error)
    ListUserOrders(userID string, page, pageSize int) ([]*Order, error)
}

// 数据模型
type Order struct {
    ID            string
    UserID        string  // 引用User Service
    Items         []OrderItem
    TotalAmount   decimal.Decimal
    Status        OrderStatus
    ShippingAddress Address
    CreatedAt     time.Time
}

type OrderItem struct {
    ProductID   string  // 引用Product Service
    ProductName string  // 冗余,避免查询Product
    Quantity    int
    Price       decimal.Decimal
    Subtotal    decimal.Decimal
}

// 数据库表
orders:
  id, user_id, total_amount, status, shipping_address, created_at

order_items:
  id, order_id, product_id, product_name, quantity, price, subtotal

3. Payment Service(支付服务)

// API接口
type PaymentService interface {
    CreatePayment(req CreatePaymentRequest) (*Payment, error)
    QueryPayment(paymentID string) (*Payment, error)
    RefundPayment(paymentID string, amount decimal.Decimal) error
}

// 数据模型
type Payment struct {
    ID            string
    OrderID       string  // 引用Order Service
    Amount        decimal.Decimal
    Method        PaymentMethod
    Status        PaymentStatus
    TransactionID string  // 第三方支付流水号
    CreatedAt     time.Time
}

// 数据库表
payments:
  id, order_id, amount, method, status, transaction_id, created_at

payment_logs:
  id, payment_id, action, result, message, created_at

服务间交互:

下单流程(Saga模式):

1. OrderService.CreateOrder()
   ↓
2. ProductService.CheckStock() [同步调用]
   ↓
3. OrderService保存订单(状态:待支付)
   ↓
4. 发布OrderCreatedEvent
   ↓
5. InventoryService监听 → 预扣库存
   ↓
6. PaymentService.CreatePayment()
   ↓
7. 发布PaymentCompletedEvent
   ↓
8. OrderService监听 → 更新订单状态
   ↓
9. InventoryService监听 → 确认扣库存

代码实现:

// 订单服务创建订单
func (svc *OrderService) CreateOrder(ctx context.Context, req CreateOrderRequest) (*Order, error) {
    // 1. 验证商品库存(同步调用Product Service)
    for _, item := range req.Items {
        available, err := svc.productClient.CheckStock(ctx, item.ProductID, item.Quantity)
        if err != nil || !available {
            return nil, errors.New("insufficient stock")
        }
    }

    // 2. 创建订单(状态:待支付)
    order := &Order{
        ID:              generateID(),
        UserID:          req.UserID,
        Items:           req.Items,
        Status:          OrderStatusPending,
        ShippingAddress: req.ShippingAddress,
        CreatedAt:       time.Now(),
    }

    // 计算总金额
    order.calculateTotal()

    // 3. 保存订单
    if err := svc.orderRepo.Save(order); err != nil {
        return nil, err
    }

    // 4. 发布事件(异步扣库存)
    event := OrderCreatedEvent{
        OrderID: order.ID,
        Items:   order.Items,
    }
    svc.eventBus.Publish(event)

    return order, nil
}

// 库存服务监听订单创建事件
func (svc *InventoryService) OnOrderCreated(event OrderCreatedEvent) error {
    // 预扣库存
    for _, item := range event.Items {
        if err := svc.ReserveStock(item.ProductID, item.Quantity); err != nil {
            // 发布库存不足事件,触发订单取消
            svc.eventBus.Publish(StockInsufficientEvent{
                OrderID:   event.OrderID,
                ProductID: item.ProductID,
            })
            return err
        }
    }

    return nil
}

面试问答

如何确定微服务的拆分粒度?

答案:

判断标准:

1. 团队维度:
    2-pizza团队(5-10人)可维护
    需要跨团队才能完成一个功能

2. 代码维度:
    5k-50k行代码
    超过10万行(考虑拆分)

3. 业务维度:
    单一业务能力
    多个业务能力混杂

4. 数据维度:
    3-15个数据表
    超过20个表(职责过多)

5. 变更维度:
    变更影响范围小
    改一个功能涉及多个服务

6. 部署维度:
    部署时间 < 15分钟
    部署复杂、耗时长

经验法则:

宁愿先大后小,不要先小后大

原因:
- 合并服务容易
- 拆分服务困难(数据迁移、事务处理)

建议:
从相对粗粒度开始,根据实际需要逐步拆分

服务间如何避免循环依赖?

答案:

识别循环依赖:

OrderService → UserService (获取用户信息)
UserService → OrderService (获取订单统计)

这就是循环依赖!

解决方案:

方案1:合并服务

如果两个服务相互依赖严重,考虑合并

OrderService + UserService → UserOrderService

适用:边界划分错误的情况

方案2:引入聚合服务

创建UserProfileService:
- 聚合用户信息
- 聚合订单统计
- 对外提供统一查询

OrderService → UserProfileService
UserService → UserProfileService

方案3:事件驱动(推荐)

// OrderService: 创建订单后发布事件
func (svc *OrderService) CreateOrder(...) {
    // ... 创建订单

    svc.eventBus.Publish(OrderCreatedEvent{
        UserID: order.UserID,
    })
}

// UserService: 监听事件,更新订单数
func (svc *UserService) OnOrderCreated(event OrderCreatedEvent) {
    svc.userRepo.IncrementOrderCount(event.UserID)
}

优势:
 异步解耦
 无循环依赖
 性能好

方案4:数据冗余

OrderService存储:
- user_id
- user_name (冗余)
- user_phone (冗余)

避免每次查询都调用UserService

适用:读多写少的场景

如何处理跨服务的数据一致性?

答案:

场景:

创建订单需要:
1. 创建订单(Order Service)
2. 扣减库存(Inventory Service)
3. 创建支付(Payment Service)

如何保证一致性?

方案对比:

方案一致性性能复杂度推荐
2PC强一致低高
Saga最终一致高中
TCC最终一致中高金融场景
本地消息表最终一致高低

推荐:Saga编排模式

// Saga协调器
type OrderSaga struct {
    orderSvc     *OrderService
    inventorySvc *InventoryService
    paymentSvc   *PaymentService
}

func (saga *OrderSaga) Execute(req CreateOrderRequest) error {
    var compensations []func() error

    // 步骤1: 创建订单
    order, err := saga.orderSvc.CreateOrder(req)
    if err != nil {
        return err
    }
    compensations = append(compensations, func() error {
        return saga.orderSvc.CancelOrder(order.ID)
    })

    // 步骤2: 扣减库存
    err = saga.inventorySvc.ReserveStock(req.Items)
    if err != nil {
        saga.compensate(compensations)
        return err
    }
    compensations = append(compensations, func() error {
        return saga.inventorySvc.ReleaseStock(req.Items)
    })

    // 步骤3: 创建支付
    payment, err := saga.paymentSvc.CreatePayment(order.ID, order.TotalAmount)
    if err != nil {
        saga.compensate(compensations)
        return err
    }

    return nil
}

func (saga *OrderSaga) compensate(compensations []func() error) {
    // 倒序执行补偿
    for i := len(compensations) - 1; i >= 0; i-- {
        compensations[i]()
    }
}

详见第4章:数据一致性方案


DDD中的限界上下文如何映射到微服务?

答案:

原则:一个限界上下文 = 一个微服务(或一组微服务)

示例:电商系统

限界上下文识别:

1. 用户上下文(User Context):
   - 核心概念:User(认证、授权)
   - 服务:User Service

2. 订单上下文(Order Context):
   - 核心概念:Order、OrderItem
   - 服务:Order Service

3. 商品上下文(Product Context):
   - 核心概念:Product、Category
   - 服务:Product Service

4. 库存上下文(Inventory Context):
   - 核心概念:Stock、Warehouse
   - 服务:Inventory Service

关键点:

1. 同一概念在不同上下文中含义不同:

   User在用户上下文:
   - ID、name、email、password

   User在订单上下文:
   - ID、name、phone、address
   (只关心收货信息)

   User在营销上下文:
   - ID、tags、preferences、behavior
   (只关心用户画像)

2. 上下文间通过ID引用:
   Order.userID → 引用User Context
   而非直接包含User对象

3. 上下文映射:
   - Shared Kernel: 共享模型
   - Customer-Supplier: 上下游关系
   - Anti-Corruption Layer: 防腐层

什么时候应该拆分服务,什么时候应该合并服务?

答案:

拆分信号:

应该拆分的情况:

1. 服务过大:
    代码超过5万行
    团队超过10人
    部署时间超过30分钟

2. 职责混乱:
    一个服务包含多个业务能力
    变更频繁互相影响

3. 扩展需求:
    某个功能需要独立扩展
    不同功能的资源需求差异大

4. 团队组织:
    团队拆分,服务也应拆分

合并信号:

应该合并的情况:

1. 服务过小:
    代码少于1000行
    只有1-2个API
    部署比开发还复杂

2. 频繁交互:
    两个服务相互调用频繁
    分布式事务复杂
    网络开销大

3. 业务内聚:
    两个服务总是一起变更
    边界不清晰

4. 运维成本:
    服务数量过多(>50个)
    运维困难

决策流程:

问自己:
1. 拆分后是否更容易维护?
2. 拆分后团队是否更自治?
3. 拆分后扩展是否更灵活?

如果3个都是"是" → 拆分
否则 → 保持现状或合并

Prev
第1章:微服务架构概述
Next
第3章:服务间通信