跳转至

🌐 分布式系统基础

⚠️ 时效性说明:本章涉及前沿模型/价格/榜单等信息,可能随版本快速变化;请以论文原文、官方发布页和 API 文档为准。

分布式系统基础拓扑图

学习时间:6小时 | 难度:⭐⭐⭐⭐ 进阶 | 前置知识:03-数据存储设计


🎯 本章目标

  • 深入理解Raft一致性协议(Leader选举、日志复制)
  • 掌握分布式锁的多种实现方案及对比
  • 了解服务注册与发现的机制
  • 理解RPC框架的工作原理
  • 掌握分布式链路追踪的基本原理

📋 目录


1. 分布式一致性协议

1.1 为什么需要一致性协议

Text Only
问题:多个节点如何就某个值达成一致?

场景1:Leader选举
  3个节点,谁当Leader?如果Leader挂了谁接任?

场景2:日志复制
  写操作发生后,如何确保多数节点都有这条数据?

场景3:配置变更
  集群配置修改,如何确保所有节点看到的配置一致?

这就是共识(Consensus)问题!

1.2 Raft协议详解

Raft是目前最流行的分布式一致性协议,被Etcd、TiKV、CockroachDB等采用。设计初衷是比Paxos更容易理解和实现。

1.2.1 核心概念

Text Only
Raft中的三种角色:

  ┌──────────┐    超时     ┌──────────┐    获得多数票    ┌──────────┐
  │ Follower │ ────────→  │ Candidate│ ─────────────→  │  Leader  │
  │ (追随者)  │ ←────────  │ (候选人)  │ ←─────────────  │ (领导者)  │
  └──────────┘   发现Leader └──────────┘   发现更高任期    └──────────┘
       ↑                                                     │
       └─────────────────────────────────────────────────────┘
                          任期结束/发现更高任期

任期(Term):
  每次选举开始一个新任期
  Term就像逻辑时钟,单调递增
  每个任期最多一个Leader

  Term 1      Term 2       Term 3       Term 4
  ┌────────┐  ┌──────────┐  ┌────────┐  ┌──────────┐
  │election│  │ election │  │no elect│  │election │
  │+normal │  │ +normal  │  │(split) │  │+normal  │
  └────────┘  └──────────┘  └────────┘  └──────────┘

1.2.2 Leader选举

Text Only
初始状态:所有节点都是Follower

选举超时(Election Timeout: 150-300ms随机):
  1. Follower在超时时间内没收到心跳
  2. 转变为Candidate
  3. 自增任期号(Term++)
  4. 投票给自己
  5. 向其他节点发送RequestVote RPC

投票规则:
  - 每个节点在每个任期只能投一票(先到先得)
  - 候选人的日志至少要和自己一样新才投票
  - 得到多数(N/2+1)票才能成为Leader

示例(5节点集群):

  Node1(Candidate): "我要竞选Term=2的Leader"
  Node2: "OK,我投你"(Term=2首次投票)
  Node3: "OK,我投你"
  Node4: 网络故障,没收到
  Node5: "OK,我投你"

  Node1收到3票(加上自己共4票 > 5/2)→ 成为Leader!
  Node1开始发送心跳给所有Follower

选举超时的随机性非常重要:
  避免所有节点同时超时 → 同时成为Candidate → 瓜分选票 → 无法选出
Python
# Raft节点状态简化实现
import random
import time
from enum import Enum
from threading import Timer, Lock

class Role(Enum):
    FOLLOWER = "follower"
    CANDIDATE = "candidate"
    LEADER = "leader"

class RaftNode:
    def __init__(self, node_id, peers):
        self.node_id = node_id
        self.peers = peers  # 其他节点列表
        self.role = Role.FOLLOWER
        self.current_term = 0
        self.voted_for = None
        self.log = []  # 日志条目列表
        self.commit_index = 0
        self.lock = Lock()

        # Leader的状态
        self.next_index = {}   # 每个follower的下一个发送index
        self.match_index = {}  # 每个follower已复制的最高index

        self._reset_election_timeout()

    def _reset_election_timeout(self):
        """重置选举超时(随机150-300ms)"""
        self.election_timeout = random.uniform(0.15, 0.3)
        self.last_heartbeat = time.time()

    def _start_election(self):
        """发起选举"""
        with self.lock:
            self.role = Role.CANDIDATE
            self.current_term += 1
            self.voted_for = self.node_id
            votes_received = 1  # 投自己一票

            # 向所有peer发送RequestVote
            for peer in self.peers:
                vote_granted = self._send_request_vote(peer)
                if vote_granted:
                    votes_received += 1

            # 检查是否获得多数票
            if votes_received > (len(self.peers) + 1) / 2:
                self._become_leader()
            else:
                self.role = Role.FOLLOWER

    def _become_leader(self):
        """成为Leader"""
        self.role = Role.LEADER
        # 初始化next_index和match_index
        for peer in self.peers:
            self.next_index[peer] = len(self.log)
            self.match_index[peer] = 0
        # 开始发送心跳
        self._send_heartbeats()

    def handle_request_vote(self, candidate_term, candidate_id, last_log_index, last_log_term):
        """处理投票请求"""
        with self.lock:
            if candidate_term < self.current_term:
                return False  # 候选人任期太旧

            if candidate_term > self.current_term:
                self.current_term = candidate_term
                self.role = Role.FOLLOWER
                self.voted_for = None

            # 检查是否已投票 + 候选人日志是否足够新
            if (self.voted_for is None or self.voted_for == candidate_id):
                if self._is_log_up_to_date(last_log_index, last_log_term):
                    self.voted_for = candidate_id
                    self._reset_election_timeout()
                    return True
            return False

    def _is_log_up_to_date(self, last_log_index, last_log_term):
        """判断候选人的日志是否至少和自己一样新"""
        if not self.log:
            return True
        my_last_term = self.log[-1]['term']  # 负索引:从末尾倒数访问元素
        my_last_index = len(self.log) - 1
        if last_log_term != my_last_term:
            return last_log_term > my_last_term
        return last_log_index >= my_last_index

1.2.3 日志复制

Text Only
Leader日志复制流程:

1. 客户端发送请求到Leader
2. Leader将命令追加到本地日志
3. Leader并行发送AppendEntries RPC到所有Follower
4. 多数Follower确认后,Leader提交该日志
5. Leader响应客户端
6. Leader通知Follower提交

日志结构:
  Index:  1    2    3    4    5    6    7
  Term:   1    1    2    2    3    3    3
  Cmd:   SET  SET  SET  DEL  SET  SET  SET
         x=1  y=2  x=3  y    z=5  w=4  v=7

日志复制示例(5节点,Term=3):

  Leader:    [1|1] [2|1] [3|2] [4|2] [5|3] [6|3] [7|3]  ← committed
  Follower1: [1|1] [2|1] [3|2] [4|2] [5|3] [6|3]        ← 差一条
  Follower2: [1|1] [2|1] [3|2] [4|2] [5|3] [6|3] [7|3]  ← 完全同步
  Follower3: [1|1] [2|1] [3|2] [4|2]                      ← 差较多
  Follower4: (网络断开)                                     ← 不可达

  已提交 = Leader + Follower1 + Follower2 = 3个节点有 → 多数确认 → 提交!

1.2.4 安全性保证

Raft通过以下机制保证安全性:

机制 保证 说明
选举限制 新Leader包含所有已提交日志 Candidate的日志必须最新才能获得投票
Leader只追加 Leader不会覆盖或删除自己的日志 只通过追加方式添加新日志
日志匹配 如果两个日志某位置相同,之前的所有位置也相同 通过AppendEntries的一致性检查保证

1.3 Paxos简述

Paxos是最早的分布式一致性算法(Lamport提出),但以难以理解和实现著称。

Text Only
Paxos角色:
  - Proposer:提出提案
  - Acceptor:接受提案
  - Learner:学习结果

基本Paxos两阶段:
  Phase 1a: Proposer发送Prepare(n)
  Phase 1b: Acceptor返回Promise,承诺不再接受<n的提案
  Phase 2a: Proposer发送Accept(n, value)
  Phase 2b: Acceptor接受提案

Multi-Paxos = 多轮Basic Paxos
  选出一个Leader(类似Raft)后,简化为一阶段

Raft vs Paxos

维度 Raft Paxos
可理解性 ✅ 高 ❌ 低
实现难度
Leader 必须有 Multi-Paxos需要
日志连续性 保证连续 可能有空洞
工业实践 Etcd/TiKV Chubby/Spanner

2. 分布式锁

2.1 为什么需要分布式锁

Text Only
单机环境:
  synchronized/Lock 即可解决并发问题

分布式环境:
  多个服务实例运行在不同机器上
  进程内的锁对其他机器无效
  → 需要一个所有机器都能访问的外部锁服务

  Server A: lock.acquire()  → 获得锁
  Server B: lock.acquire()  → 等待...
  Server C: lock.acquire()  → 等待...

分布式锁的要求: - 互斥性:同一时刻只有一个客户端持有锁 - 安全性:锁只能由持有者释放 - 可用性:锁服务本身要高可用 - 防死锁:即使持有者崩溃,锁也能最终释放 - 可重入(可选):同一客户端可重复获取

2.2 Redis分布式锁

基础实现

Python
import redis
import uuid
import time

class RedisDistributedLock:
    def __init__(self, redis_client, lock_name, expire_seconds=10):
        self.redis = redis_client
        self.lock_name = f"lock:{lock_name}"
        self.expire = expire_seconds
        self.identifier = str(uuid.uuid4())  # 唯一标识,防止误释放

    def acquire(self, timeout=10):
        """获取锁"""
        end_time = time.time() + timeout
        while time.time() < end_time:
            # SET key value NX EX seconds(原子操作)
            if self.redis.set(self.lock_name, self.identifier,
                            nx=True, ex=self.expire):
                return True
            time.sleep(0.01)  # 短暂等待后重试
        return False

    def release(self):
        """释放锁(Lua脚本保证原子性)"""
        lua_script = """
        if redis.call('get', KEYS[1]) == ARGV[1] then
            return redis.call('del', KEYS[1])
        else
            return 0
        end
        """
        return self.redis.eval(lua_script, 1, self.lock_name, self.identifier)

# 使用示例
lock = RedisDistributedLock(redis.Redis(), "order:12345")
if lock.acquire():
    try:  # try/except捕获异常
        # 临界区代码
        process_order()
    finally:
        lock.release()

Redis锁的问题

问题 说明 解决方案
锁过期但业务未完成 锁自动过期,别人获得锁 看门狗(Watchdog)自动续期
Redis单点故障 Redis挂了锁就没了 RedLock算法(N个独立Redis)
Redis主从切换 主节点写入锁后未同步到从节点就挂了 RedLock / 使用强一致存储

Redisson看门狗机制

Text Only
获取锁 → 启动看门狗线程 → 每锁TTL/3时间续期
                         锁TTL设30秒
                         每10秒续期一次
                         直到锁被显式释放

2.3 ZooKeeper分布式锁

Text Only
ZooKeeper锁利用临时顺序节点实现:

1. 每个客户端在/lock下创建临时顺序节点
   /lock/node-0001  ← Client A
   /lock/node-0002  ← Client B
   /lock/node-0003  ← Client C

2. 判断自己是否是最小节点
   Client A: node-0001 是最小的 → 获得锁!
   Client B: node-0002 不是最小 → 监听node-0001
   Client C: node-0003 不是最小 → 监听node-0002

3. Client A释放锁(删除node-0001或会话断开自动删除)
   Client B收到通知 → 检查自己是否最小 → 获得锁!

优势:
  - 不需要设置过期时间(会话断开自动删除)
  - 天然公平锁(按创建顺序)
  - 基于ZK的强一致性保证

劣势:
  - 性能较低(需要创建和删除ZNode)
  - ZK集群运维复杂
  - 网络抖动可能误判会话断开
Java
// ZooKeeper分布式锁(使用Curator框架)
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;

public class ZkLockExample {
    private final InterProcessMutex lock;

    public ZkLockExample(CuratorFramework client) {
        this.lock = new InterProcessMutex(client, "/locks/order");
    }

    public void processWithLock() throws Exception {
        if (lock.acquire(10, TimeUnit.SECONDS)) {
            try {  // try/catch捕获异常
                // 临界区代码
                processOrder();
            } finally {
                lock.release();
            }
        }
    }
}

2.4 Etcd分布式锁

Text Only
Etcd锁基于租约(Lease)和版本号(Revision):

1. 创建租约(Lease)
   lease_id = etcd.grant(ttl=30)  # 30秒租约

2. 在锁路径下创建Key(带租约)
   etcd.put("/lock/my-lock", "client-1", lease=lease_id)

3. 根据Revision判断是否获得锁
   最小Revision的获得锁

4. 自动续约(KeepAlive)
   保持租约不过期

5. 释放锁
   删除Key 或 撤销租约

2.5 三种方案对比

维度 Redis ZooKeeper Etcd
一致性 AP(主从异步) CP(ZAB协议) CP(Raft协议)
性能 ⭐⭐⭐⭐⭐ 最高 ⭐⭐ 较低 ⭐⭐⭐ 中等
可靠性 ⭐⭐⭐ 可能丢锁 ⭐⭐⭐⭐ 高 ⭐⭐⭐⭐⭐ 最高
实现复杂度 中(用Curator)
锁过期 需要设置TTL 会话自动管理 租约自动管理
公平性 非公平 天然公平 天然公平
watch机制 不支持 支持 支持
运维 简单 复杂 中等
适用场景 高性能、容忍偶尔丢锁 强一致性要求 云原生环境

选型建议

Text Only
Redis锁:
  适合:高并发场景、性能优先、锁丢失影响小
  示例:减库存、限流、防重复提交

ZooKeeper锁:
  适合:强一致性要求、已有ZK集群
  示例:Leader选举、分布式协调

Etcd锁:
  适合:云原生环境、Kubernetes生态
  示例:K8s组件协调、分布式任务调度

3. 分布式事务补充

3.1 消息事务(RocketMQ事务消息)

Text Only
RocketMQ事务消息流程:

  Producer                     Broker                    Consumer
     │                          │                          │
     │ 1. 发送半消息(Half Msg) │                          │
     │─────────────────────────→│                          │
     │                          │ (半消息对Consumer不可见)   │
     │ 2. 执行本地事务           │                          │
     ├── 成功                   │                          │
     │                          │                          │
     │ 3. Commit               │                          │
     │─────────────────────────→│ 4. 投递消息              │
     │                          │─────────────────────────→│
     │                          │                          │ 5. 消费处理
     │                          │                          │

如果Commit/Rollback消息丢失:
     │                          │
     │    Broker主动回查          │
     │←─────────────────────────│  "你的本地事务成功了吗?"
     │    返回状态               │
     │─────────────────────────→│

4. 服务注册与发现

4.1 为什么需要服务发现

Text Only
没有服务发现:                    有服务发现:
  Service A → 硬编码IP:PORT      Service A → 注册中心 → Service B

  问题:                          优势:
  - IP变了要改配置重启            - 自动发现服务实例
  - 扩容缩容要手动修改            - 自动感知上下线
  - 无法做健康检查                - 支持负载均衡

4.2 服务发现架构

Text Only
  ┌──────────┐    注册     ┌──────────────┐    注册     ┌──────────┐
  │Service A │ ─────────→  │  注册中心      │ ←───────── │Service B │
  │ 实例1    │             │              │             │ 实例1    │
  │ 实例2    │ ←─────────  │  Eureka /    │ ──────────→ │ 实例2    │
  │ 实例3    │    发现      │  Consul /    │    发现     │ 实例3    │
  └──────────┘             │  Nacos       │             └──────────┘
                           └──────────────┘
                             心跳健康检查

4.3 主流服务发现对比

特性 Eureka Consul Nacos
开发商 Netflix HashiCorp Alibaba
一致性协议 AP(对等复制) CP(Raft) + AP AP(Distro) + CP(Raft)
健康检查 心跳 HTTP/TCP/gRPC/脚本 心跳+主动探测
负载均衡 Ribbon(客户端) DNS/HTTP 内置
配置中心 ❌ 不支持 ✅ KV存储 ✅ 原生支持
多数据中心
SpringCloud ✅ 原生
K8s集成
语言 Java Go Java
适用场景 Java微服务 多语言/K8s 阿里生态/国内

选型建议

Text Only
选Eureka:简单Java微服务项目,对一致性要求不高
选Consul:多语言、需要KV配置、K8s环境
选Nacos:国内场景、需要配置中心、阿里云生态

5. RPC框架

5.1 什么是RPC

Text Only
RPC(Remote Procedure Call)= 远程过程调用
让调用远程服务像调用本地函数一样简单

本地调用:
  result = calculator.add(1, 2)  // 直接调方法

RPC调用:
  result = calculator.add(1, 2)  // 看起来一样,但实际上...

  底层过程:
  1. 客户端stub序列化参数
  2. 通过网络发送到远程服务器
  3. 服务端stub反序列化参数
  4. 调用真实方法
  5. 序列化返回值
  6. 通过网络返回客户端
  7. 客户端stub反序列化返回值
Text Only
RPC调用过程:

Client                                 Server
┌─────────┐                           ┌─────────┐
│ 业务代码 │                           │ 业务代码 │
│ add(1,2)│                           │ add(a,b)│
└────┬────┘                           └────▲────┘
     │                                     │
┌────▼────┐                           ┌────┴────┐
│Client   │    1.序列化  2.网络传输      │Server  │
│Stub     │ ─────────────────────────→ │Stub     │
│         │ ←───────────────────────── │         │
└─────────┘    4.反序列化 3.网络传输     └─────────┘

5.2 主流RPC框架对比

特性 gRPC Thrift Dubbo
开发商 Google Facebook→Apache Alibaba→Apache
IDL Protocol Buffers Thrift IDL Java接口
传输协议 HTTP/2 TCP TCP(默认)/HTTP
序列化 Protobuf Thrift Binary/Compact Hessian2/JSON/Protobuf
流式传输 ✅ 4种模式
多语言 ✅ 10+语言 ✅ 16+语言 ⭕ 主要Java
服务治理 需要搭配Envoy/Istio ✅ 内置丰富
负载均衡 客户端/代理 ✅ 内置多种
生态 云原生/K8s 大数据 Java微服务
性能

5.3 gRPC详解

Protocol Buffer
// 定义服务(.proto文件)
syntax = "proto3";

package hello;

// 服务定义
service Greeter {
  // 一元RPC
  rpc SayHello (HelloRequest) returns (HelloReply);

  // 服务端流式
  rpc ListFeatures (Rectangle) returns (stream Feature);

  // 客户端流式
  rpc RecordRoute (stream Point) returns (RouteSummary);

  // 双向流式
  rpc RouteChat (stream RouteNote) returns (stream RouteNote);
}

message HelloRequest {
  string name = 1;
}

message HelloReply {
  string message = 1;
}
Python
# gRPC Python服务端
import grpc
from concurrent import futures
import hello_pb2
import hello_pb2_grpc

class GreeterServicer(hello_pb2_grpc.GreeterServicer):
    def SayHello(self, request, context):
        return hello_pb2.HelloReply(message=f"Hello, {request.name}!")

def serve():
    server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))  # 线程池/多线程:并发执行任务
    hello_pb2_grpc.add_GreeterServicer_to_server(GreeterServicer(), server)
    server.add_insecure_port('[::]:50051')  # 切片操作:[start:end:step]提取子序列
    server.start()
    server.wait_for_termination()
Go
// gRPC Go服务端
package main

import (
    "context"
    "log"
    "net"
    "google.golang.org/grpc"
    pb "example/hello"
)

type server struct {
    pb.UnimplementedGreeterServer
}

func (s *server) SayHello(ctx context.Context, req *pb.HelloRequest) (*pb.HelloReply, error) {
    return &pb.HelloReply{Message: "Hello " + req.GetName()}, nil
}

func main() {
    lis, _ := net.Listen("tcp", ":50051")
    s := grpc.NewServer()
    pb.RegisterGreeterServer(s, &server{})
    log.Fatal(s.Serve(lis))
}

5.4 gRPC四种通信模式

Text Only
1. 一元RPC(Unary):
   Client ──request──→ Server
   Client ←──reply───← Server

2. 服务端流式(Server Streaming):
   Client ──request──→ Server
   Client ←──reply1──← Server
   Client ←──reply2──← Server
   Client ←──reply3──← Server

3. 客户端流式(Client Streaming):
   Client ──request1──→ Server
   Client ──request2──→ Server
   Client ──request3──→ Server
   Client ←──reply────← Server

4. 双向流式(Bidirectional Streaming):
   Client ──request1──→ Server
   Client ←──reply1───← Server
   Client ──request2──→ Server
   Client ←──reply2───← Server

6. 分布式链路追踪

6.1 为什么需要链路追踪

Text Only
微服务调用链:
  用户请求 → API Gateway → 订单服务 → 库存服务
                                   → 支付服务 → 风控服务
                                              → 银行接口
                        → 用户服务
                        → 推荐服务

问题:
  - 请求慢了,慢在哪个服务?
  - 请求失败了,失败在哪一步?
  - 调用链路是什么样的?
  - 各服务的延迟分布?

这就是分布式链路追踪要解决的问题!

6.2 核心概念

Text Only
Trace(链路):一次完整请求的调用链
  TraceID: abc-123

Span(跨度):链路中的一个操作
  SpanID: span-1, span-2, ...
  ParentSpanID: 父span

示例:
  Trace: abc-123
  ├── Span 1: API Gateway (root span)
  │   ├── Span 2: 订单服务
  │   │   ├── Span 3: 库存服务
  │   │   └── Span 4: 支付服务
  │   │       └── Span 5: 风控服务
  │   └── Span 6: 用户服务
  时间线:
  Span 1: |========================|
  Span 2:    |=================|
  Span 3:       |======|
  Span 4:               |========|
  Span 5:                 |====|
  Span 6:    |====|

6.3 主流方案对比

特性 Jaeger Zipkin SkyWalking
开发商 Uber→CNCF Twitter Apache(华为)
语言 Go Java Java
协议 OpenTelemetry B3 自定义+OTel
存储 Cassandra/ES/Kafka Cassandra/ES/MySQL ES/H2/MySQL
采样 自适应采样 固定/限流 固定/自适应
UI ✅(丰富)
监控 基本 基本 ✅ 内置Metrics
K8s ✅ 原生
侵入性 低(OpenTelemetry) 低(Agent)

当前趋势:OpenTelemetry正在统一链路追踪标准

Text Only
OpenTelemetry = OpenTracing + OpenCensus 合并

提供统一的:
  - API规范
  - SDK实现
  - Collector(收集器)
  - Exporter(导出到各种后端)

7. 练习与延伸阅读

7.1 练习题

  1. Raft模拟:手动模拟5节点Raft集群的Leader选举过程:
  2. 初始状态所有节点都是Follower
  3. Node3首先超时,发起选举
  4. 在选举过程中Node5网络分区
  5. 画出每个节点的状态变化

  6. 分布式锁选型:为以下场景选择合适的分布式锁方案并说明理由:

  7. 电商秒杀扣减库存 > Redis分布式锁(RedLock) —— 性能极高(微秒级)、支持自动过期防死锁、适合短时高并发场景。秒杀对性能敏感,ZK锁太慢。
  8. 分布式定时任务调度 > ZooKeeper分布式锁 —— 强一致性保证任务不重复执行、临时顺序节点天然支持公平排队。定时任务对延迟不敏感,优先保证正确性。
  9. K8s集群Leader选举 > etcd分布式锁(Lease+Revision) —— K8s本身使用etcd,无需引入额外组件。etcd基于Raft强一致性,适合Leader选举场景。

  10. 服务发现:设计一个简化的服务注册与发现系统,支持:

  11. 服务注册(注册名、IP、端口、健康检查URL)
  12. 服务发现(按名称查找所有实例)
  13. 心跳检测(超时自动摘除)

  14. 链路追踪:分析以下链路追踪数据,找出性能瓶颈:

    Text Only
       Span A (总时长: 500ms)
       ├── Span B: 订单服务 (50ms)
       ├── Span C: 库存服务 (30ms)
       ├── Span D: 支付服务 (350ms)
       │   ├── Span E: 风控 (50ms)
       │   └── Span F: 银行接口 (280ms)
       └── Span G: 通知服务 (20ms)
    

    瓶颈:Span F(银行接口 280ms),占总时长56%。支付服务D占350ms(70%),其中银行接口是主要原因。优化方向:①银行接口异步化(先返回"processing",回调通知结果) ②通知服务G可与D并行执行(而非串行) ③加缓存减少放控重复查询。

7.2 延伸阅读

  • 《DDIA》第8章 — 分布式系统的问题
  • 《DDIA》第9章 — 一致性与共识
  • Raft论文: "In Search of an Understandable Consensus Algorithm"
  • Raft可视化: https://raft.github.io
  • Martin Kleppmann: "How to do distributed locking"
  • gRPC官方文档: https://grpc.io
  • OpenTelemetry官方文档

📝 本章小结

知识点 关键要点
Raft Leader选举(多数票) + 日志复制(多数确认) + 安全性保证
分布式锁 Redis高性能但可能丢锁、ZK/Etcd强一致但性能较低
服务发现 Eureka(AP)/Consul(CP)/Nacos(AP+CP)
RPC gRPC(高性能+流式)/Dubbo(Java生态+服务治理)
链路追踪 Trace→Span层级、OpenTelemetry统一标准

上一章:数据存储设计 | 下一章:高可用与容灾