Skip to content

分布式系统组件全景与学习路线

基础理论面试闭环:网络失败、CAP、一致性、共识与幂等标准回答。详细原理仍保留在各知识页,避免面试答案覆盖推导过程。

共识算法专题:Paxos、Raft、ZAB 与多数派主线 · etcd/raft 状态机、持久化、线性读与故障恢复 · 独立面试题。专题从论文不变量追到 v3.7.0 源码,并用 v3.6.0 真实三节点测试验证 PreVote、分区、冲突覆盖、恢复和 ReadIndex。

新增专题:Kubernetes服务发现:Service、EndpointSlice、CoreDNS与流量传播原理 · 面试标准回答。该专题从微服务调用视角解释地址快照、连接级负载、滚动摘流,以及Java 8/Boot 2与Java 17+/Boot 3接入边界;包级网络细节仍归入DevOps Kubernetes网络专栏。

Service Mesh 专题:Envoy、xDS、mTLS 与流量治理主线 · Envoy 运行时、xDS 收敛与生产治理 · 独立面试题。专题把请求还原到 Listener、HCM、Route、Cluster、Endpoint 和连接池,并覆盖 Warming、Panic、Outlier、SDS、Ambient、商业配置与 Runbook。

gRPC专题:协议与生产主线 · Java内部原理与生产治理 · 独立面试题。专题把Stub调用还原为Resolver、Picker、Subchannel、物理Attempt、HTTP/2 Stream、服务端Executor、业务事务与Trailers,并覆盖流式背压和长连接倾斜。

新增专题:微服务稳定性治理:超时、重试、隔离、熔断、过载保护与故障演练 · 面试标准回答。该专题从一次慢依赖的事故传播出发,解释每个保护器的位置、原理、误用后果与恢复过程。

新增专题:微服务容量规划:SLO、压测、瓶颈识别、扩缩容与容量闭环 · 面试标准回答。专题把 QPS、RT、P95/P99、Little 定律、线程池、连接池、DB、Redis、MQ、HPA 和扩容无效排查串成生产容量闭环。

新增专题:故障演练与混沌工程:故障注入、爆炸半径、稳态指标与恢复验证 · 面试标准回答。专题把超时、重试、熔断、限流、降级、幂等、补偿、MQ堆积和灾备验证串成可执行演练闭环。

新增专题:微服务可观测性:OpenTelemetry、Trace传播、采样、指标基数与SLO · 面试标准回答。主线从应用埋点追踪到Collector和后端,并解释异步因果、遥测丢失窗口与SLO告警。

新增专题:多机房与异地多活:单元化、RPO/RTO、故障切换、脑裂与回切 · 面试标准回答。主线围绕业务写所有权和切换状态机,而不是把两个机房误写成双活。

新增专题:API与事件契约治理:REST、OpenAPI、Protobuf、Schema Registry与兼容发布 · 面试标准回答。专题围绕新旧Reader/Writer共存、历史消息、灾备与回滚窗口建立可执行契约。

新增专题:微服务组件选型与能力归属:REST、Dubbo、gRPC、MQ、Gateway、Spring Cloud与Mesh · 面试标准回答。专题重点消除双层负载、多层重试、超时倒挂和安全职责混淆。

新增专题:微服务网关治理:路由、认证授权、限流、灰度、错误语义与排查 · 面试标准回答。专题从统一入口讲到 OAuth2/OIDC、JWT、下游授权、限流 key、lb:// 负载均衡和 401/403/404/429/503/504 排查。

新增专题:分布式会话与状态管理:Session、JWT、Redis、粘性会话与无状态服务 · 面试标准回答。专题讲清登录态跨实例恢复、JWT撤销、权限版本、Redis Session、WebSocket连接状态和用户串号排查。

新增专题:微服务配置治理:配置中心、动态刷新、灰度、回滚、密钥与排查 · 面试标准回答。专题把配置中心放回控制面语境,讲清启动加载、运行期刷新、资源切换、版本收敛和配置事故 Runbook。

新增专题:CDC与Outbox:MySQL到MQ、ES、Redis的可靠同步与重放 · 面试标准回答。专题把本地消息表、binlog/WAL、MQ、ES、缓存、位点、重复、乱序、死信和重建索引串成数据收敛链路。

新增专题:多租户隔离:身份、数据权限、资源配额、缓存、MQ与数据库治理 · 面试标准回答。专题覆盖可信租户身份、Header清洗、Service数据权限、SQL兜底、热租户隔离、缓存Key、MQ和大租户独立化。

一、学完本专栏要真正会什么

分布式系统不是“用了微服务、Redis、MQ 就算分布式”。只要一次业务操作跨越进程、机器、数据库或网络,就要面对单机程序没有的事实:消息会延迟,节点会宕机,响应会丢失,时钟会漂移,同一请求可能执行多次,不同节点可能同时认为自己正确。

本专栏的目标不是背组件,而是让你能够完成下面这些工作:

  1. 从一次远程调用判断连接、发送、执行、提交、响应各阶段的失败窗口。
  2. 解释超时为什么只能证明“没有按时得到答案”,不能证明下游没执行。
  3. 根据业务语义设计幂等键、状态机、唯一约束、补偿和对账。
  4. 解释 CAP、BASE、线性一致性、最终一致性、Quorum、Raft、ZAB 的适用层次。
  5. 区分注册中心、配置中心、协调服务、RPC、MQ、分布式事务、锁和限流。
  6. 对 XA、TCC、Saga、事务消息、Seata 作有前提的选型,而不是问“哪个最好”。
  7. 解释 Redis、ZooKeeper、etcd 锁的锁所有权、租约、会话和 Fencing Token。
  8. 设计网关、服务、租户和下游资源的多层限流预算。
  9. 用 Trace、Metrics、Logs、业务流水、协调器状态和中间件指标定位线上故障。
  10. 清楚每个方案“不这样做会怎样”,并能写出最小可运行 Demo。

二、什么才叫分布式系统

一个系统的组件位于不同故障域,彼此通过不可靠通信协作,并共同维护业务结果,就构成了分布式系统。这里的“不同故障域”很重要:同一 JVM 内的方法通常一起存活;两个服务进程可能一个成功、一个宕机;两个机房可能因专线故障互相不可见但仍各自运行。

mermaid
flowchart TD
    A["一个业务操作"] --> B["订单服务与订单库"]
    A --> C["库存服务与库存库"]
    A --> D["支付服务与支付渠道"]
    A --> E["消息队列与异步消费者"]
    B --> F["各节点可独立成功、失败或超时"]
    C --> F
    D --> F
    E --> F
    F --> G["系统必须把部分结果收敛为可解释的最终状态"]

分布式的收益与代价必须一起看:

收益为什么有价值同时增加的代价
横向扩容增加实例处理更多流量负载均衡、状态共享、热点和容量规划
故障隔离一个服务故障不必拖垮全部系统超时、熔断、降级和依赖治理
独立发布团队可按业务边界演进接口兼容、灰度、版本和数据迁移
数据分治不同模型选择不同存储跨库一致性、对账、搜索与缓存同步
异地容灾单机房故障仍能服务网络分区、复制延迟、冲突解决

如果业务规模和团队规模没有这些需求,单体或模块化单体往往更可靠。微服务不是技术成熟度勋章,而是用额外复杂度换取明确收益。

三、分布式系统的八个硬事实

3.1 网络有三种结果,不是两种

本地方法常被理解成成功或抛异常;远程调用至少有:

  • 明确成功:收到可验证的成功响应。
  • 明确失败:收到业务拒绝或协议明确失败。
  • 结果未知:超时、断连、进程重启,调用方不知道服务端是否已经提交。
mermaid
flowchart TD
    A["调用方发送扣款请求"] --> B{"请求是否到达服务端"}
    B -- "未到达" --> C["服务端未执行"]
    B -- "到达" --> D{"本地事务是否提交"}
    D -- "未提交" --> E["业务未生效"]
    D -- "已提交" --> F{"响应是否到达调用方"}
    F -- "到达" --> G["调用方知道成功"]
    F -- "丢失或超时" --> H["调用方只知道结果未知"]

因此,SocketTimeoutException 不能直接映射成“扣款失败”。正确做法通常是使用同一业务幂等键查询事实状态,必要时安全重试。

3.2 节点故障和网络分区难以区分

节点没有回应,可能是进程死了、GC 停顿、CPU 打满、网络拥塞、交换机故障或只是响应迟到。故障检测器只能在超时时间后“怀疑”节点,不能瞬间证明节点永久死亡。

深入学习:分布式故障检测:心跳、Phi Accrual、SWIM 与健康探针 · 独立面试题

3.3 请求会重复

客户端重试、网关重试、MQ 重投、消费者 Rebalance、定时任务补跑、支付回调和协调器恢复都会产生重复。至少一次投递是可靠系统的常见现实,因此所有有副作用的入口都要设计幂等。

3.4 状态会短暂分叉

订单库已提交、缓存未删除、ES 未更新、消息仍在路上时,不同读取路径会看到不同结果。系统必须定义允许多长时间、谁是事实源、怎样修复,而不是只说“最终会一致”。

3.5 时钟不是绝对可靠的全局顺序

机器时钟可能漂移或被 NTP 调整。System.currentTimeMillis() 适合记录近似现实时间,但不能单独证明跨节点事件的因果顺序,也不适合直接当全局唯一 ID。租约和超时还要考虑时钟单调性及服务端裁决。

深入学习:分布式时间与时钟:NTP、Deadline、租约、Fencing 与回拨 · 独立面试题

3.6 局部成功不等于全局成功

三个步骤中两个提交、一个失败,不能用一个布尔值表达。要用状态机记录过程,例如 CREATED → STOCK_RESERVED → PAYING → PAID,并允许进入 COMPENSATINGMANUAL_REVIEW

3.7 重试会放大故障

A 调 B 最多 3 次,B 调 C 最多 3 次,C 最坏收到 9 次。若下游越慢上游越重试,会形成正反馈雪崩。重试必须有总时间预算、次数上限、退避、抖动、幂等和熔断。

3.8 任何协调机制也有失败边界

注册中心会不可用,Redis 锁会过期,ZooKeeper Session 会失效,事务协调器会恢复,MQ 会重复投递。组件减少某类不确定性,却不会取消分布式事实。

四、分布式系统六层知识地图

mermaid
flowchart TD
    A["01 通信层<br/>HTTP、RPC、MQ、协议、序列化"] --> B["02 寻址与协调层<br/>Nacos、Consul、ZooKeeper、etcd"]
    B --> C["03 流量治理层<br/>Gateway、负载均衡、限流、熔断、隔离"]
    C --> D["04 状态一致性层<br/>幂等、事务、缓存、CDC、状态机"]
    D --> E["05 复制与共识层<br/>Quorum、Paxos、Raft、ZAB"]
    E --> F["06 运行保障层<br/>发布、容灾、观测、补偿、对账"]

这六层不是严格网络协议栈,而是学习顺序。先不理解通信失败,就无法理解幂等;先不理解一致性,就无法正确选择事务;先不理解故障证据,就无法做生产排查。

五、常见组件全景与职责边界

5.1 服务寻址和配置

组件核心职责典型数据不应该承担
Nacos注册发现、配置管理服务实例、Namespace、Group、DataId业务请求转发和大量业务数据
Eureka经典客户端注册发现服务名、实例、状态、元数据配置管理和强一致协调
Consul服务发现、健康检查、KVCatalog、Health、KV高频业务数据库
Kubernetes Service/EndpointSlice集群内服务寻址Pod 地址、端口、就绪状态应用级业务事务
Spring Cloud Config/Apollo配置治理版本化应用配置订单、库存等实时业务状态

注册中心通常位于控制面:客户端订阅或拉取实例并形成本地快照,业务请求由 HTTP/RPC 客户端直达提供者。每次业务请求同步查注册中心会把控制面变成数据面瓶颈。

5.2 协调与共识

组件/算法解决的问题核心机制典型用途
ZooKeeper分布式协调ZAB、Session、临时/顺序节点、Watcher选主、锁、元数据协调
etcd强一致 KV 与协调Raft、Revision、Lease、Watch、事务比较Kubernetes 状态、配置、选主、锁
Consul服务治理和 KVRaft、Agent、健康检查、Gossip、Connect多语言服务发现和基础设施治理
Raft复制状态机共识Leader、Term、日志复制、多数派etcd、控制面元数据
Paxos/Multi-Paxos共识理论与实现族Proposal、Promise、Accept、多数派分布式数据库和协调系统基础
ZABZooKeeper 原子广播epoch、zxid、Proposal、ACK、COMMITZooKeeper 写入和恢复

共识解决“多个副本在故障下如何对一串状态变化达成一致”,不直接解决订单和支付跨服务的业务事务。业务事务可能借助共识系统保存协调状态,但两者层次不同。

5.3 服务通信

方式优点代价合适场景
HTTP/REST通用、跨语言、易调试文本协议开销、契约约束需治理对外 API、通用微服务调用
OpenFeign声明式 HTTP、Spring 集成好容易误以为是本地方法Spring 服务内部同步调用
Dubbo完整 RPC、协议与治理扩展框架和契约治理复杂Java 内部高频 RPC
gRPCProtobuf 契约、流式、跨语言浏览器/调试和网关适配成本高性能跨语言内部调用
MQ异步、削峰、解耦、可重放最终一致、重复、顺序、积压事件传播和非即时步骤

同步调用提供即时结果,但把可用性和延迟绑定到下游;异步消息隔离峰值,却要求状态机、幂等、重试、死信和对账。

5.4 流量治理

能力保护对象常见实现与其他能力的区别
负载均衡多个健康实例LoadBalancer、Dubbo、Nginx、Envoy选择目标,不限制总流量
限流自身容量和公平配额Sentinel、Gateway、Redis Lua、Envoy超过预算时主动拒绝或排队
并发隔离线程、连接、信号量Bulkhead、线程池、信号量限制同时占用资源数量
熔断不健康下游Sentinel、Resilience4j、Envoy根据失败/慢调用暂时阻断
降级非核心能力fallback、静态数据、异步化选择较弱但可接受的业务结果
超时单次等待预算HTTP/RPC/DB/MQ 客户端配置防止一次调用无限占用资源

这些能力必须按时间预算组合。例如入口总预算 1 秒,下游不能每个都配置 1 秒再叠加两次重试。

5.5 数据一致性和事务

方案一致性思路适用主要风险
XA/JTA/2PC参与者准备后统一提交资源支持 XA、短事务、强一致要求持锁、阻塞、协调器恢复、in-doubt
TCC业务预留资源后 Confirm/Cancel高价值短流程、可设计预留侵入、空回滚、悬挂、幂等
Saga正向步骤和业务补偿长流程、允许中间状态补偿失败、补偿非物理回滚
本地消息表/Outbox业务状态和待发事件同库提交大多数最终一致事件投递延迟、重复、表清理
MQ 事务消息协调本地事务和消息可见性Broker 支持且语义匹配回查、重复消费、消费者一致性
SeataAT/XA/TCC/Saga 集成协调Java 生态的明确场景模式约束、热点、协调器和恢复
CDC从数据库日志捕获变化数据集成、ES/数仓同步顺序、Schema 演进、重复、回放

没有一种方案能同时做到零侵入、无限吞吐、任意资源、强一致和永不阻塞。选型必须从业务不变量、可接受中间状态、吞吐、延迟和补偿能力出发。

5.6 锁、租约和 Fencing Token

分布式锁不是“把 synchronized 放到 Redis”。锁服务要回答:

  1. 谁获得了锁,所有权如何标识。
  2. 锁何时过期,客户端暂停时怎样处理。
  3. 释放时如何防止删除别人的锁。
  4. 网络分区后旧持有者是否仍能写资源。
  5. 下游资源如何拒绝过期持有者。
mermaid
flowchart TD
    A["客户端 A 获得锁与 token=10"] --> B["A 长时间 GC,租约过期"]
    B --> C["客户端 B 获得锁与 token=11"]
    C --> D["A 恢复并继续提交旧操作"]
    D --> E{"资源是否校验递增 token"}
    E -- "不校验" --> F["旧持有者可能覆盖新结果"]
    E -- "校验" --> G["拒绝 token=10 的过期写入"]

Fencing Token 是单调递增的世代号。锁服务只保证互斥窗口并不总能阻止暂停后的旧客户端继续操作;真正的数据资源若能校验 Token,才能拒绝僵尸持有者。

六、一次商业请求的完整分布式生命周期

以“提交订单”为例:

mermaid
flowchart TD
    A["客户端生成业务幂等键"] --> B["Gateway 认证、限流、路由"]
    B --> C["订单服务原子占用幂等键"]
    C --> D["订单库事务创建状态机记录和 Outbox"]
    D --> E["库存服务按订单号预占库存"]
    E --> F["订单事务推进到 STOCK_RESERVED"]
    F --> G["Outbox 发布 OrderCreated 事件"]
    G --> H["支付、履约、通知、ES 各自幂等消费"]
    H --> I["对账任务检查长时间未收敛状态"]

每一步都要回答四个问题:

  • 请求重复会不会多执行一次副作用?
  • 超时后事实源在哪里查询?
  • 当前状态允许哪些合法迁移?
  • 自动修复失败后怎样告警和人工处理?

七、超时、重试和总时间预算

一次调用通常包含:连接池等待、DNS、连接、TLS、请求发送、服务端排队、业务执行和响应读取。只设置一个模糊的 timeout=3s,排查时无法知道时间花在哪里。

text
入口剩余预算
= 连接池等待
+ 建连/TLS
+ 服务端排队与执行
+ 响应传输
+ 重试退避
+ 上游处理和安全余量

如果入口 SLA 是 1000ms,可以按业务实测分配预算,例如订单自身 150ms、库存 250ms、支付单 300ms、网络与序列化 100ms、余量 200ms。数字必须来自压测和生产分位数,不是通用固定值。

适合重试的条件通常同时包括:

  • 错误被明确分类为瞬时错误。
  • 操作幂等,或服务端支持同一幂等键结果复用。
  • 总时间预算仍充足。
  • 使用退避和随机抖动,避免客户端同时重试。
  • 只在一个经过设计的层负责重试。

参数校验失败、余额不足、库存不足、权限拒绝不应重试。结果未知的写请求应先查询事实状态,而不是换一个请求号重新执行。

八、幂等、状态机、唯一约束为什么要组合

幂等键标识“这是同一次业务意图”;唯一约束负责在并发下原子拒绝重复;状态机负责限制当前状态能执行的动作;结果表负责让重复请求复用第一次结果。

mermaid
flowchart TD
    A["收到 idempotencyKey"] --> B["原子插入请求记录"]
    B --> C{"是否首次获得处理权"}
    C -- "是" --> D["校验当前业务状态并执行"]
    D --> E["同一事务写业务结果和成功状态"]
    C -- "否" --> F{"已有状态"}
    F -- "SUCCEEDED" --> G["返回第一次结果"]
    F -- "PROCESSING" --> H["返回处理中或查询事实源"]
    F -- "FAILED_RETRYABLE" --> I["按租约和 owner token 接管"]

只做“先查数据库是否存在,再插入”会有并发竞态;只加分布式锁但没有唯一约束,锁故障时仍可能重复;只用唯一约束却不保存响应,重复请求无法得到一致结果。

深入学习:请求、消息、任务与状态机幂等

九、从 CAP 到业务最终一致的逻辑链

CAP 讨论网络分区发生时,一致性与可用性如何取舍;它不是数据库产品广告标签,也不是所有业务的“三选二”。BASE 描述允许中间状态并通过后续过程收敛的工程思路。

业务最终一致必须具备可验证闭环:

mermaid
flowchart TD
    A["事实源事务提交"] --> B["可靠记录待传播事件"]
    B --> C["异步投递并允许重试"]
    C --> D["消费者幂等更新派生状态"]
    D --> E["失败进入重试、死信或补偿"]
    E --> F["对账比较事实源与派生状态"]
    F --> G["自动修复或人工处理"]
    G --> H["指标证明在承诺时间内收敛"]

如果没有重试、死信、对账、修复、告警和收敛指标,“最终一致”只是希望,不是设计。

十、共识、复制和业务事务不能混为一谈

复制是把状态保存到多个副本;共识是在节点失败和消息延迟下,对操作顺序或值达成一致;业务事务是维护订单、库存、支付等业务不变量。

问题典型答案
etcd 三副本怎样确定一次写提交Raft Leader 将日志复制到多数派并满足提交规则
ZooKeeper 写请求怎样形成一致顺序Leader 通过 ZAB Proposal、ACK、COMMIT 广播事务
订单和库存两个数据库怎样一致TCC、Saga、可靠消息、Seata或业务补偿
为什么注册中心能高可用服务端复制、健康模型和客户端本地缓存共同作用

Raft 不能直接替你撤销一笔支付;Saga 也不能替数据库副本选 Leader。理解层次后,组件边界才不会混乱。

深入学习:Paxos、Raft、ZAB 与多数派共识 · Raft内部原理与生产治理 · 独立面试题

十一、数据库、缓存、ES 与 CDC 的一致性链路

数据库通常是交易事实源,Redis 是性能视图,ES 是搜索视图。一次商品更新不可能用普通本地事务原子提交三个异构系统,因此要设计传播和修复:

mermaid
flowchart TD
    A["MySQL 提交商品版本 version=8"] --> B["Outbox 或 Binlog CDC 捕获变化"]
    B --> C["发布带主键和版本号的事件"]
    C --> D["Redis 消费者删除或重建缓存"]
    C --> E["ES 消费者按版本更新文档"]
    D --> F["失败重试与缓存自然过期"]
    E --> G["失败重试、死信和索引重建"]
    F --> H["对账任务比较事实源版本"]
    G --> H

事件必须携带业务主键和版本。否则旧事件晚到时可能覆盖新数据。ES 更新失败不能只打印日志,要保留可重试事件、监控 Lag、进入死信,并支持按数据库事实源重放或重建索引。

深入学习:缓存一致性 · 数据一致性设计

十二、分布式 ID 为什么不是简单自增

分库分表后单库自增只能保证库内唯一。常见 ID 方案:

方案原理优点风险
UUID随机/时间等构造 128 位标识无中心、生成方便长、索引局部性差、不同版本语义不同
数据库号段中心表批量分配区间趋势递增、吞吐高号段服务和步长治理
Snowflake 类时间戳 + 节点号 + 序列本地高吞吐、趋势递增时钟回拨、节点号冲突、位宽寿命
Redis INCR单线程原子递增简单递增可用性、持久化和跨地域延迟

Snowflake 遇到时钟回拨时,不能无条件继续按较小时间戳生成,否则可能与历史 ID 冲突。常见处理包括短回拨等待、维护逻辑时间、切换备用节点位、长回拨拒绝服务并告警。

深入学习:分布式 ID:UUID、数据库号段、Snowflake 与生产治理 · 独立面试题

数据规模继续增长时,还需要把 Key 映射到稳定逻辑分区并安全迁移。深入学习:数据分片与一致性哈希 · 独立面试题

服务实例扩缩容还需要理解连接级与请求级选择。深入学习:微服务负载均衡:P2C、EWMA、Maglev 与长连接治理 · 独立面试题

十三、Service Mesh 把什么下沉到了基础设施

Istio、Linkerd、Envoy 等可以把部分服务发现、mTLS、路由、重试、熔断、指标采集下沉到 Sidecar 或节点代理。它减少语言 SDK 差异,但不会替代业务幂等、状态机、事务和错误语义。

mermaid
flowchart TD
    A["Service A 业务进程"] --> B["本地代理"]
    B --> C["mTLS、路由、负载、重试、指标"]
    C --> D["Service B 代理"]
    D --> E["Service B 业务进程"]
    F["控制面下发配置"] --> B
    F --> D

Mesh 自动重试写请求同样可能重复副作用。代理看到的是 HTTP/gRPC 状态码,不一定理解“扣款已提交但响应丢失”的业务事实。

深入学习:Service Mesh 主线 · Envoy 运行时、xDS 收敛与生产治理 · 独立面试题

十四、商业常用场景与方案组合

场景核心不变量推荐组合必须防范
下单扣库存同一订单不能重复占库存状态机、幂等键、条件更新、TCC或Saga超卖、空回滚、超时未知
支付回调同一渠道流水只入账一次签名、唯一索引、状态条件更新、对账重复回调、伪造、金额不一致
商品同步 ES搜索最终反映数据库版本Outbox/CDC、版本事件、重试、重建索引旧事件覆盖新文档、更新丢失
多租户 API一个租户不能占满系统租户令牌桶、全局并发限制、公平队列高基数 key、Redis 热点、误伤
医疗采集断点续传且数据不重不漏到可接受范围任务分片、采集批次、幂等落库、补偿上游重复、顺序乱、敏感日志
定时结算多实例只能处理一次业务批次调度分片、业务唯一键、租约、Fencing锁过期后双写、任务悬挂
秒杀库存不超卖且系统不崩CDN、网关限流、令牌、队列削峰、库存预扣热点、超卖、重复下单

十五、分布式故障排查总 Runbook

15.1 先定义故障而不是先重启

记录开始时间、影响租户/接口/地域、成功率、P95/P99、错误码和最近变更。重启可能暂时释放资源,却会丢失现场、触发重连风暴或 MQ Rebalance。

15.2 按链路分层取证

mermaid
flowchart TD
    A["入口成功率与 Trace 样本"] --> B["Gateway 路由、限流、连接池"]
    B --> C["调用方线程池、超时、重试、实例选择"]
    C --> D["提供者 CPU、GC、线程、队列和业务错误"]
    D --> E["MySQL锁与慢SQL、Redis延迟、MQ Lag"]
    E --> F["协调器、注册中心、网络和基础设施"]
    F --> G["业务流水、幂等记录、事务/消息状态收口"]

15.3 常见症状对应证据

症状优先证据不要只做什么
无可用实例客户端本地快照、Namespace/Group、实例健康和注册 IP只看注册中心控制台
大量超时Trace 分段耗时、连接池等待、线程池队列、下游 P99只把超时调大
重复订单幂等表、唯一键冲突、网关/客户端重试、MQ 投递次数只怪用户重复点击
消息堆积Queue/Partition Lag 分布、消费耗时、失败重试、下游容量只扩容消费者
数据不一致事实源版本、Outbox/CDC 位点、消费记录、死信、对账差异手工直接改多个库
锁失效双写锁 token、租约、GC 停顿、资源端版本校验只看 Redis key 是否存在
事务悬挂XID、全局/分支状态、协调器日志、锁等待、补偿记录直接删协调记录

十六、JDK 8 Demo:结果未知状态机

下面用纯 JDK 8 展示为什么超时不能直接标记失败。第一次请求在服务端提交后“响应丢失”;调用方进入 UNKNOWN,使用同一幂等键查询后才确认成功。

java
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class UnknownResultStateDemo {
    enum ClientState { NEW, UNKNOWN, SUCCEEDED, FAILED_FINAL }

    static final class PaymentService {
        private final Map<String, String> results =
                new ConcurrentHashMap<String, String>();

        String pay(String idempotencyKey, boolean loseResponse) {
            String old = results.putIfAbsent(idempotencyKey, "PAID");
            String result = old == null ? "PAID" : old;
            if (loseResponse) {
                throw new RuntimeException("response lost after commit");
            }
            return result;
        }

        String query(String idempotencyKey) {
            return results.get(idempotencyKey);
        }
    }

    public static void main(String[] args) {
        PaymentService service = new PaymentService();
        String key = "pay:ORDER-9001";
        ClientState state = ClientState.NEW;

        try {
            service.pay(key, true);
            state = ClientState.SUCCEEDED;
        } catch (RuntimeException timeoutOrDisconnect) {
            state = ClientState.UNKNOWN;
        }

        System.out.println("afterCall=" + state);
        String fact = service.query(key);
        if ("PAID".equals(fact)) {
            state = ClientState.SUCCEEDED;
        }
        System.out.println("fact=" + fact + ", final=" + state);
    }
}

运行结果:

text
afterCall=UNKNOWN
fact=PAID, final=SUCCEEDED

真实系统的查询可能来自支付流水表、渠道查询接口或消息状态。关键不是捕获某一种异常,而是把“不确定”作为合法状态,并能根据事实源收敛。

十七、常见反模式与后果

反模式为什么错误线上后果
把远程接口当本地方法忽略网络、序列化和部分失败线程堆积、重复副作用、错误误判
所有层都自动重试尝试次数乘法放大下游雪崩、重复写
用 traceId 当幂等键每次重试可能产生新 Trace同一业务被执行多次
最终一致不做对账无法证明最终收敛静默脏数据长期存在
分布式锁没有唯一约束锁过期或网络异常仍可并发重复记录、超卖
MQ 消费成功后再单独记幂等业务提交与去重记录不原子重复消费时再次执行
配置中心保存业务状态配置系统不适合高频事务并发覆盖、审计和一致性混乱
只看平均耗时尾延迟被掩盖少量慢请求拖垮线程池
故障先重启再取证现场丢失且可能扩大抖动根因反复出现

十八、按依赖关系学习,不按组件热度学习

  1. 分布式系统基础:远程调用、超时、重试、状态机和可观测性。
  2. CAP、BASE 与一致性理论:理解可用性、一致性和网络分区的约束。
  3. 数据一致性设计幂等全过程:掌握业务收敛基本功。
  4. Paxos、Raft、ZAB 与多数派:理解协调系统和复制的底层依据。
  5. ZooKeeperDubbo:学习协调组件和 RPC 完整调用链。
  6. 分布式事务:比较 XA、TCC、Saga、可靠消息和 Seata。
  7. CDC与Outbox数据同步:MySQL 到 MQ、ES、Redis 的事件传播、位点、重放和对账。
  8. 缓存一致性与数据库、ES、CDC 传播链路。
  9. 分布式锁与 Fencing Token,再学习 Redlock 的争议和边界。
  10. 网关治理、认证授权与灰度:入口路由、OAuth2/JWT、下游授权、限流、灰度和状态码排查。
  11. 分布式会话与状态管理:Session、Redis、JWT、粘性会话、权限版本、WebSocket和用户串号排查。
  12. 分布式限流:算法、多实例共享状态和多层容量治理。
  13. 多租户隔离与资源配额:身份、数据权限、SQL、缓存、MQ、热租户和大租户独立化。
  14. 容量规划、压测与扩缩容:SLO、P95/P99、Little 定律、容量拐点、HPA 和扩容无效排查。
  15. 故障演练与混沌工程:故障注入、爆炸半径、稳态指标、停止条件、恢复验证和复盘闭环。
  16. 配置治理、动态刷新与回滚:控制面、版本快照、灰度发布、密钥、回滚和配置事故排查。
  17. 商业场景训练营生产级主线验收清单
  18. 最后使用分布式面试页复习标准回答,并跳回精确原理锚点。

十九、面试标准回答

分布式系统把状态和计算放在多个独立故障域,通过网络协作。它的核心难点不是组件数量,而是网络不可靠、结果可能未知、请求可能重复、状态会短暂分叉、节点时钟和故障判断不完美。工程上要通过超时预算、有限重试、幂等键、唯一约束、状态机、限流熔断、事务或可靠消息、补偿对账以及 Trace/Metrics/Logs 让系统在部分失败时仍能得到可解释并可修复的结果。注册中心、RPC、MQ、共识、锁和分布式事务分别解决不同层次的问题,不能互相替代。

本章小结

学习分布式真正的主线是:先承认不确定性,再定义业务不变量,然后选择能提供证据、限制损失并最终收敛的机制。组件只是这些机制的实现。后续每个专栏都应该能够回答“内部怎么执行、在哪一步失败、失败后事实在哪里、怎样恢复”。