如何设计微服务

2022-12-022 出链
  • S: 单体架构是构建应用的传统方式:所有功能打包在一个进程里,开发、部署、调试简单直接
  • C: 随着业务增长和团队扩大,单体变成大泥球,导致构建慢、部署风险高、局部扩展难实现(团队之间互相阻塞)
  • Q: 如何在保持交付速度的同时,让系统具备独立演进弹性扩展的能力?
  • A: 按业务能力拆分为一组小型、自治的服务(微服务),每个服务独立开发、部署、扩展,通过轻量级通信协作

引出了几个问题:怎么拆?如何通信?如何管理数据?如何独立交付和运行?

怎么拆

核心特征

特征 说明
单一职责 一个服务只做一件事,围绕一个上下文建模
自治 独立开发、测试、部署和回滚
去中心化治理 团队自治,但遵守统一的交付和安全底线
可独立替换 服务可以被重写而不影响其他服务
为失败设计 假设网络不可靠,做好熔断、重试、降级

拆分策略:拆分粒度没有标准答案。过细导致"分布式单体",过粗又退化为单体。经验法则:一个服务能被一个小团队(2-pizza team)拥有和理解

  1. 按业务能力(Business Capability):订单、支付、库存各自成服务。最常用的是组织架构对齐(康威定律)
  2. 按子域(Subdomain):用 领域驱动设计 识别 bounded context,核心域、支撑域、通用域,分别建服务
  3. Strangler Fig 模式:从单体边缘逐步抽取服务;迁移期间明确数据的唯一事实来源,避免长期双写

服务边界的验收标准不是足够小,而是能否独立变更、部署和回滚。若仍需多个服务锁步发布,就是分布式单体。

可靠性

  • 调用下游(包括内部服务和第三方 API):
    • 超时:限制对下游的等待时间,避免请求无限占用资源
    • 重试:只重试瞬时故障和幂等操作,并使用指数退避与抖动,避免重试风暴
    • 熔断:下游持续故障时快速失败,防止故障沿调用链扩散
    • 舱壁:隔离不同下游使用的连接池、线程池等资源,限制故障半径
  • 接收上游调用(包括内部调用方和第三方):
    • 认证与授权:确认调用方身份及其访问权限
    • 限流与配额:限制单个调用方或租户的流量
    • 负载卸载:系统过载时尽早拒绝请求,保护核心能力
    • 舱壁:避免单个调用方耗尽共享资源
    • 幂等:避免调用方重试产生重复副作用

熔断主要保护调用方,限流主要保护被调用方;舱壁在两个方向都可以使用。

服务之间如何通信

同步

方式 特点
REST / HTTP 简单通用,适合 CRUD 场景
gRPC 强类型 + 高性能,适合内部服务间调用

异步

方式 特点
消息队列(Kafka / RabbitMQ) 解耦生产者和消费者,削峰填谷
事件驱动(Event-Driven) 服务发布领域事件,其他服务订阅响应

必须立即获得结果时使用同步;允许延迟、需要削峰或一对多通知时使用异步。两者是在选择不同的耦合和失败方式。

契约演进

  • API 和事件应隐藏内部数据模型,并保持向后兼容
  • 使用扩展式变更,确认消费者迁移后再删除旧字段或版本
  • 用消费者驱动契约测试验证服务能否独立发布

服务治理

  • 服务发现:Consul / etcd / K8s DNS
  • 负载均衡:客户端(gRPC)或服务端(Nginx / Envoy)
  • API Gateway:统一入口,处理认证、限流、路由
  • Service Mesh(Istio / Linkerd):将通信治理下沉到 sidecar,应用代码无感知

服务数据管理

  • 每个服务拥有自己的数据,其他服务只能通过 API 或事件访问;不强制使用独立数据库实例
  • 跨服务查询:用 API Composition 或 CQRS
  • 分布式事务:
    • 避免两阶段提交(2PC):性能和可用性代价高
    • Saga:用一系列本地事务与补偿操作实现最终一致性,但不提供事务隔离
      • 协同式(Choreography):参与者通过事件协作
      • 编排式(Orchestration):由编排器显式驱动流程
    • 补偿不是回滚,而是新的业务操作;它必须可重试、幂等,并允许人工介入
  • 可靠消息:
    • Transactional Outbox:在同一本地事务中写业务数据和待发送消息
    • 幂等消费者:用消息 ID 或业务幂等键处理至少一次投递产生的重复消息

交付与测试

  • 每个服务拥有独立流水线,能够独立构建、测试、部署和回滚
  • 测试以服务内测试和契约测试为主,只保留少量关键路径的端到端测试
  • 跨职能团队端到端拥有服务;平台提供部署、可观测性和安全的统一能力
  • 服务间调用也要验证身份和权限,不默认内部网络可信

可观测性(LTM: Log/Trace/Metric)

  • 分布式追踪(OpenTelemetry / Jaeger):跟踪请求在多个服务间的链路。
  • 集中日志(ELK / Loki):统一收集、关联日志。
  • 指标监控(Prometheus + Grafana):延迟、错误率、吞吐量。
  • 使用 trace ID、message ID、业务 ID 关联信号,围绕用户旅程定义 SLO 和告警

你真的需要微服务吗?

微服务不是目标,独立交付能力才是目标。如果模块化单体(Modular Monolith)能满足需求,就不需要微服务。

  • 团队较小、系统简单,且没有独立交付、扩展或故障隔离需求时,模块化单体成本更低
  • 领域边界不清晰,过早拆分只会反复重构
  • 没有自动化基础设施(CI/CD、容器编排、监控),运维成本会吞噬开发效率
  • 低延迟要求极高的场景(如高频交易),进程间通信开销不可接受
  1. https://martinfowler.com/articles/microservices.html
  2. https://martinfowler.com/microservices
  3. https://microservices.io/patterns
  4. https://learn.microsoft.com/zh-cn/azure/architecture/patterns/strangler-fig

评论