🌐 分布式系统基础¶
⚠️ 时效性说明:本章涉及前沿模型/价格/榜单等信息,可能随版本快速变化;请以论文原文、官方发布页和 API 文档为准。
学习时间:6小时 | 难度:⭐⭐⭐⭐ 进阶 | 前置知识:03-数据存储设计
🎯 本章目标¶
- 深入理解Raft一致性协议(Leader选举、日志复制)
- 掌握分布式锁的多种实现方案及对比
- 了解服务注册与发现的机制
- 理解RPC框架的工作原理
- 掌握分布式链路追踪的基本原理
📋 目录¶
1. 分布式一致性协议¶
1.1 为什么需要一致性协议¶
问题:多个节点如何就某个值达成一致?
场景1:Leader选举
3个节点,谁当Leader?如果Leader挂了谁接任?
场景2:日志复制
写操作发生后,如何确保多数节点都有这条数据?
场景3:配置变更
集群配置修改,如何确保所有节点看到的配置一致?
这就是共识(Consensus)问题!
1.2 Raft协议详解¶
Raft是目前最流行的分布式一致性协议,被Etcd、TiKV、CockroachDB等采用。设计初衷是比Paxos更容易理解和实现。
1.2.1 核心概念¶
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选举¶
初始状态:所有节点都是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 → 瓜分选票 → 无法选出
# 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 日志复制¶
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提出),但以难以理解和实现著称。
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 为什么需要分布式锁¶
单机环境:
synchronized/Lock 即可解决并发问题
分布式环境:
多个服务实例运行在不同机器上
进程内的锁对其他机器无效
→ 需要一个所有机器都能访问的外部锁服务
Server A: lock.acquire() → 获得锁
Server B: lock.acquire() → 等待...
Server C: lock.acquire() → 等待...
分布式锁的要求: - 互斥性:同一时刻只有一个客户端持有锁 - 安全性:锁只能由持有者释放 - 可用性:锁服务本身要高可用 - 防死锁:即使持有者崩溃,锁也能最终释放 - 可重入(可选):同一客户端可重复获取
2.2 Redis分布式锁¶
基础实现:
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看门狗机制:
2.3 ZooKeeper分布式锁¶
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集群运维复杂
- 网络抖动可能误判会话断开
// 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分布式锁¶
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机制 | 不支持 | 支持 | 支持 |
| 运维 | 简单 | 复杂 | 中等 |
| 适用场景 | 高性能、容忍偶尔丢锁 | 强一致性要求 | 云原生环境 |
选型建议:
Redis锁:
适合:高并发场景、性能优先、锁丢失影响小
示例:减库存、限流、防重复提交
ZooKeeper锁:
适合:强一致性要求、已有ZK集群
示例:Leader选举、分布式协调
Etcd锁:
适合:云原生环境、Kubernetes生态
示例:K8s组件协调、分布式任务调度
3. 分布式事务补充¶
3.1 消息事务(RocketMQ事务消息)¶
RocketMQ事务消息流程:
Producer Broker Consumer
│ │ │
│ 1. 发送半消息(Half Msg) │ │
│─────────────────────────→│ │
│ │ (半消息对Consumer不可见) │
│ 2. 执行本地事务 │ │
├── 成功 │ │
│ │ │
│ 3. Commit │ │
│─────────────────────────→│ 4. 投递消息 │
│ │─────────────────────────→│
│ │ │ 5. 消费处理
│ │ │
如果Commit/Rollback消息丢失:
│ │
│ Broker主动回查 │
│←─────────────────────────│ "你的本地事务成功了吗?"
│ 返回状态 │
│─────────────────────────→│
4. 服务注册与发现¶
4.1 为什么需要服务发现¶
没有服务发现: 有服务发现:
Service A → 硬编码IP:PORT Service A → 注册中心 → Service B
问题: 优势:
- IP变了要改配置重启 - 自动发现服务实例
- 扩容缩容要手动修改 - 自动感知上下线
- 无法做健康检查 - 支持负载均衡
4.2 服务发现架构¶
┌──────────┐ 注册 ┌──────────────┐ 注册 ┌──────────┐
│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 | 阿里生态/国内 |
选型建议:
5. RPC框架¶
5.1 什么是RPC¶
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反序列化返回值
RPC调用过程:
Client Server
┌─────────┐ ┌─────────┐
│ 业务代码 │ │ 业务代码 │
│ add(1,2)│ │ add(a,b)│
└────┬────┘ └────▲────┘
│ │
┌────▼────┐ ┌────┴────┐
│Client │ 1.序列化 2.网络传输 │Server │
│Stub │ ─────────────────────────→ │Stub │
│ │ ←───────────────────────── │ │
└─────────┘ 4.反序列化 3.网络传输 └─────────┘
5.2 主流RPC框架对比¶
| 特性 | gRPC | Thrift | Dubbo |
|---|---|---|---|
| 开发商 | 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详解¶
// 定义服务(.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;
}
# 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()
// 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四种通信模式¶
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 为什么需要链路追踪¶
微服务调用链:
用户请求 → API Gateway → 订单服务 → 库存服务
→ 支付服务 → 风控服务
→ 银行接口
→ 用户服务
→ 推荐服务
问题:
- 请求慢了,慢在哪个服务?
- 请求失败了,失败在哪一步?
- 调用链路是什么样的?
- 各服务的延迟分布?
这就是分布式链路追踪要解决的问题!
6.2 核心概念¶
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 | Apache(华为) | |
| 语言 | Go | Java | Java |
| 协议 | OpenTelemetry | B3 | 自定义+OTel |
| 存储 | Cassandra/ES/Kafka | Cassandra/ES/MySQL | ES/H2/MySQL |
| 采样 | 自适应采样 | 固定/限流 | 固定/自适应 |
| UI | ✅ | ✅ | ✅(丰富) |
| 监控 | 基本 | 基本 | ✅ 内置Metrics |
| K8s | ✅ 原生 | ✅ | ✅ |
| 侵入性 | 低(OpenTelemetry) | 中 | 低(Agent) |
当前趋势:OpenTelemetry正在统一链路追踪标准
OpenTelemetry = OpenTracing + OpenCensus 合并
提供统一的:
- API规范
- SDK实现
- Collector(收集器)
- Exporter(导出到各种后端)
7. 练习与延伸阅读¶
7.1 练习题¶
- Raft模拟:手动模拟5节点Raft集群的Leader选举过程:
- 初始状态所有节点都是Follower
- Node3首先超时,发起选举
- 在选举过程中Node5网络分区
-
画出每个节点的状态变化
-
分布式锁选型:为以下场景选择合适的分布式锁方案并说明理由:
- 电商秒杀扣减库存 > Redis分布式锁(RedLock) —— 性能极高(微秒级)、支持自动过期防死锁、适合短时高并发场景。秒杀对性能敏感,ZK锁太慢。
- 分布式定时任务调度 > ZooKeeper分布式锁 —— 强一致性保证任务不重复执行、临时顺序节点天然支持公平排队。定时任务对延迟不敏感,优先保证正确性。
-
K8s集群Leader选举 > etcd分布式锁(Lease+Revision) —— K8s本身使用etcd,无需引入额外组件。etcd基于Raft强一致性,适合Leader选举场景。
-
服务发现:设计一个简化的服务注册与发现系统,支持:
- 服务注册(注册名、IP、端口、健康检查URL)
- 服务发现(按名称查找所有实例)
-
心跳检测(超时自动摘除)
-
链路追踪:分析以下链路追踪数据,找出性能瓶颈:
Text OnlySpan 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统一标准 |
← 上一章:数据存储设计 | 下一章:高可用与容灾 →