跳转到内容

架构与权衡

架构决策没有"正确答案",只有权衡。本章讲模块化单体、微服务的收益与运维税、同步调用与消息队列、事件驱动与最终一致——并给出选择架构的决策依据(团队规模、变更频率、一致性需求),而非流行度。

先把模块边界做好,再考虑是否拆进程:单体不等于大泥球——模块化单体(清晰的模块边界、依赖方向单向)在团队小、变更集中的阶段是最优架构:

  • 部署一次(无分布式问题)、事务简单(本地事务)、调试直观(一个进程、一个栈);
  • 微服务解决的是组织问题,代价是分布式系统的所有问题——团队 < 20 人时,单体 + 模块边界几乎总是更快。

模块边界的纪律(→ 设计原则与模式):模块间只走公开接口、依赖单向、数据库不共享表(将来拆分的预演)。

微服务的收益:

  • 独立部署:各服务独立发版(解耦发布节奏);
  • 独立扩展:热点服务单独扩容(→ 系统设计工具箱);
  • 团队自治:康威定律的顺势利用(团队边界 = 服务边界)。

运维税(分布式系统的全部问题):

拆分的时机判断:不是"什么时候该微服务",而是"单体什么时候疼"——部署互相阻塞(一个团队的改动阻塞所有发布)、扩展需求分化(某模块要扩 10 倍)、团队规模超沟通上限。先疼再拆,拆的是已经清楚的边界。

服务间通信的两条路:

同步(HTTP/RPC)异步(消息队列)
语义请求-响应,立刻知道结果发布-订阅,最终送达
耦合时间耦合(对方必须活着)时间解耦(对方可以离线)
失败传播级联(下游挂拖垮上游)隔离(队列缓冲)
一致性简单(同步链路)最终一致

消息队列解耦时间,也引入重复与乱序:至少一次投递让消息可能重复(消费端幂等,→ 协调服务与实践模式)、乱序(按 key 分区保序)。

选择:需要"立刻知道结果"(查询、扣款)→ 同步;需要"事后处理"(通知、统计、派生数据)→ 异步。混合是常态:核心链路同步,旁路异步。

事件驱动架构(EDA):状态变化发布为事件,其他服务订阅派生:

  • 事件溯源(event sourcing):事件是真相源,状态是重放——审计免费(历史完整),代价是查询要投影与版本演化复杂;
  • CQRS:写模型与读模型分离(写优化与读优化各自的存储)——读模型由事件投影,最终一致;
  • 派生数据:搜索索引、缓存、报表都由事件流派生(Kafka 的设计哲学)。

事件驱动与最终一致绑定:派生数据滞后于真相源——业务要接受"读到几分钟前的投影"。回报是扩展性与解耦(新增消费者不动生产者)。

选择架构的标准不是流行度,是四个变量:

  1. 团队规模:< 20 人单体优先;更大按团队边界拆(康威定律);
  2. 变更频率:模块变更节奏分化 → 拆分解耦发布;
  3. 扩展需求:负载不均(某模块 10 倍流量)→ 独立扩展;
  4. 一致性需求:强一致链路长 → 收窄事务边界(按聚合拆)。

先做好模块边界,再考虑是否拆进程——边界清楚的模块化单体可以逐块演进成微服务("巨石渐进拆分"),边界不清楚的直接拆微服务是分布式大泥球。

  • 微服务解决的是组织问题,代价是分布式系统的所有问题;小团队单体优先
  • 拆分时机:部署阻塞、扩展分化、团队超上限——先疼再拆
  • 消息队列解耦时间,也引入重复与乱序;核心链路同步、旁路异步
  • 事件驱动绑定最终一致:派生数据滞后,回报是扩展性与解耦
  • 决策依据:团队规模、变更频率、扩展需求、一致性需求——不是流行度