跳转至

分布式系统

概述

分布式系统是由多个通过网络协作的独立计算节点组成的系统。本文涵盖分布式系统的核心理论(CAP、一致性模型、共识算法)和工程实践(微服务、消息队列、分布式事务)。


1. CAP 定理

CAP 定理指出,分布式系统在面对网络分区(Partition) 时,不可能同时满足以下三个属性:

属性 含义
C (Consistency) 所有节点看到相同的数据
A (Availability) 每个请求都能收到(非错误的)响应
P (Partition Tolerance) 系统在网络分区时仍能运行

由于网络分区在实际中不可避免,实质上是在 CPAP 之间选择:

选择 含义 代表系统
CP 保证一致性,牺牲可用性 ZooKeeper, etcd, HBase
AP 保证可用性,牺牲一致性 Cassandra, DynamoDB, CouchDB

PACELC 扩展

PACELC 定理进一步指出:即使没有分区(E=Else),也需要在延迟(Latency)一致性(Consistency) 之间权衡。


2. 一致性模型

2.1 强一致性(Strong/Linearizability)

所有操作表现为全局顺序执行,读操作总是返回最新的写入值。

\[ \text{write}(x, v) \prec \text{read}(x) \Rightarrow \text{read}(x) = v \]
  • 实现:单主节点 + 同步复制
  • 代价:延迟高
  • 代表:Spanner(TrueTime)

2.2 最终一致性(Eventual Consistency)

如果没有新的写入,最终所有副本将收敛到相同的值。

\[ \lim_{t \to \infty} \text{replicas}(x, t) \to \text{same value} \]
  • 窗口期内可能读到旧值
  • 代表:DNS、Amazon S3、Cassandra

2.3 因果一致性(Causal Consistency)

保证因果相关的操作按因果顺序被所有节点观察到,但并发操作可以任意排序。

进程 A: write(x=1) → write(y=2)
进程 B: 如果看到 y=2,则必须已经看到 x=1
  • 实现:向量时钟(Vector Clock)
  • 比强一致性弱,比最终一致性强

3. 共识算法

共识问题:多个节点就某个值达成一致。

3.1 Paxos

Paxos 是最经典的共识算法,分为三个角色:

角色 职责
Proposer 提出提案(值)
Acceptor 接受/拒绝提案
Learner 学习被选定的值

两阶段过程:

Phase 1 (Prepare):
  Proposer → Acceptor: Prepare(n)
  Acceptor → Proposer: Promise(n, accepted_value)
  (Acceptor 承诺不再接受编号小于 n 的提案)

Phase 2 (Accept):
  Proposer → Acceptor: Accept(n, value)
  Acceptor → Proposer: Accepted(n, value)
  (多数 Acceptor 接受 → 值被选定)

3.2 Raft

Raft 是对 Paxos 的简化,更易于理解和实现。

Leader 选举

                    超时
Follower ───────────────→ Candidate
    ↑                        │
    │ 发现新 Leader           │ 获得多数票
    │                        ▼
    └──────────────────── Leader
                    ↑        │
                    │ 发现更高任期
                    └────────┘ → Follower

选举规则:

  1. Follower 超时未收到心跳 → 转为 Candidate
  2. Candidate 增加任期号(Term),向所有节点请求投票
  3. 获得多数投票 → 成为 Leader
  4. 每个任期最多一个 Leader

日志复制

Client → Leader: 写入请求
Leader: 追加到本地日志
Leader → Followers: AppendEntries RPC
Followers: 追加到本地日志,回复确认
Leader: 收到多数确认 → 提交(commit)
Leader → Client: 返回成功
Leader → Followers: 通知提交

一致性保证:

  • 如果两个日志在某个索引处的任期号相同,则该索引之前的所有条目完全一致
  • Leader 绝不会覆盖自己的日志
  • 只有日志至少和多数节点一样新的 Candidate 才能赢得选举

4. 微服务架构

4.1 服务发现

┌──────────────┐
│ Service Registry │ ← 服务注册中心
│ (Consul/etcd)    │
└──────┬───────┘
       │ 注册/心跳/查询
  ┌────┼────┐
  ▼    ▼    ▼
 [A]  [B]  [C]  ← 微服务实例
方式 实现 代表
客户端发现 客户端直接查询注册中心 Netflix Eureka
服务端发现 负载均衡器查询注册中心 AWS ALB, Consul
DNS 发现 通过 DNS 解析服务地址 Kubernetes Service

4.2 API Gateway

API 网关是微服务的统一入口:

Client → [API Gateway] → Service A
                       → Service B
                       → Service C

功能:
- 路由:根据 URL/Header 路由到不同服务
- 认证:统一的 JWT/OAuth 验证
- 限流:Rate Limiting
- 熔断:Circuit Breaker
- 日志:请求链路追踪

代表工具:Kong、Nginx、AWS API Gateway、Envoy。

4.3 熔断器模式(Circuit Breaker)

防止级联失败:

状态机:
         失败率超过阈值
Closed ──────────────→ Open
  ↑                      │
  │ 探测成功             │ 超时
  │                      ▼
  └──────────────── Half-Open
       探测失败 → Open
class CircuitBreaker:
    CLOSED, OPEN, HALF_OPEN = 0, 1, 2

    def __init__(self, failure_threshold=5, timeout=30):
        self.state = self.CLOSED
        self.failures = 0
        self.threshold = failure_threshold
        self.timeout = timeout
        self.last_failure_time = None

    def call(self, func, *args):
        if self.state == self.OPEN:
            if time.time() - self.last_failure_time > self.timeout:
                self.state = self.HALF_OPEN
            else:
                raise CircuitBreakerOpen()

        try:
            result = func(*args)
            self._on_success()
            return result
        except Exception as e:
            self._on_failure()
            raise

5. 消息队列

5.1 Kafka

Apache Kafka 是高吞吐、持久化的分布式消息流平台。

核心概念:

概念 说明
Topic 消息分类
Partition Topic 的分区(并行单元)
Producer 消息生产者
Consumer 消息消费者
Consumer Group 消费者组(一个分区只被组内一个消费者消费)
Offset 消息在分区中的位置
Broker Kafka 服务器节点
Producer → Topic [P0|P1|P2] → Consumer Group
                                ├── Consumer 1 ← P0
                                ├── Consumer 2 ← P1
                                └── Consumer 3 ← P2

特点:

  • 消息持久化到磁盘(顺序读写,高吞吐)
  • 支持消息回溯(重新消费历史消息)
  • 分区实现水平扩展

5.2 RabbitMQ

基于 AMQP 协议的传统消息队列。

Producer → Exchange → Queue → Consumer
           (路由规则)

Exchange 类型:
- Direct:  精确匹配 routing key
- Fanout:  广播到所有绑定队列
- Topic:   通配符匹配 routing key
- Headers: 根据消息头匹配

5.3 Kafka vs RabbitMQ

维度 Kafka RabbitMQ
模型 发布-订阅(日志) 消息队列
吞吐量 百万级/秒 万级/秒
消息保留 持久化,可回溯 消费后删除
顺序保证 分区内有序 队列内有序
适用场景 事件流、日志、大数据 任务队列、RPC

6. 分布式事务

6.1 两阶段提交(2PC)

协调者                  参与者A    参与者B
  │── Prepare ──────→   │          │
  │                     │── OK ──→ │
  │← OK ───────────     │          │
  │                     │          │
  │── Commit ───────→   │          │
  │                     │── ACK ─→ │
  │← ACK ──────────     │          │

问题:

  • 阻塞:参与者等待协调者决定
  • 单点故障:协调者挂掉,参与者阻塞
  • 数据不一致:网络分区时可能产生不一致

6.2 Saga 模式

将大事务拆分为一系列本地事务,每个事务有对应的补偿操作

T1 → T2 → T3 → ... → Tn
                 ↓ 失败
C3 ← C2 ← C1 ← ...   (补偿)

示例:订单流程

1. 创建订单 (T1)          补偿: 取消订单 (C1)
2. 扣减库存 (T2)          补偿: 恢复库存 (C2)  
3. 扣款 (T3)              补偿: 退款 (C3)
4. 发送通知 (T4)          补偿: 发送取消通知 (C4)

如果 T3 失败: 执行 C2 → C1

两种编排方式:

方式 实现 优势 劣势
编排式 (Choreography) 事件驱动,服务间直接通信 解耦 难以追踪
协调式 (Orchestration) 中央协调器控制流程 清晰 协调器复杂

7. 分布式系统设计原则

原则 含义
幂等性 同一操作执行多次与执行一次效果相同
超时与重试 设置合理超时,配合指数退避重试
优雅降级 部分功能不可用时,核心功能仍可运行
可观测性 日志、指标、链路追踪(Logging, Metrics, Tracing)
最终一致 接受短暂不一致,保证最终收敛

与其他主题的关系

  • 参见 系统设计,理解服务拆分、缓存、消息队列与容量规划如何连接到分布式场景
  • 参见 数据库系统,理解复制、事务、一致性与存储层设计的关系
  • 参见 云服务,理解分布式系统如何依赖云平台上的弹性与基础设施能力
  • 参见 计算机网络,理解协议、延迟与可靠传输对分布式行为的影响

参考文献

  • "Designing Data-Intensive Applications" - Martin Kleppmann
  • "Distributed Systems" - Maarten van Steen & Andrew Tanenbaum
  • Raft 论文: "In Search of an Understandable Consensus Algorithm"
  • Kafka 官方文档

评论 #