1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
| class RaftLogReplication:
"""Raft日志复制"""
def __init__(self, node: RaftNode):
self.node = node
async def replicate_log(self, command: str) -> bool:
"""复制日志到所有节点"""
if self.node.state != NodeState.LEADER:
return False
# 添加到本地日志
new_entry = LogEntry(
index=self.node.get_last_log_index() + 1,
term=self.node.current_term,
command=command
)
self.node.log.append(new_entry)
print(f"[Leader {self.node.node_id}] 添加日志: Index {new_entry.index}, Term {new_entry.term}")
# 复制到所有节点
success_count = 1 # 包括Leader自己
tasks = []
for peer in self.node.peers:
if peer.node_id != self.node.node_id:
tasks.append(self._replicate_to_peer(peer))
results = await asyncio.gather(*tasks, return_exceptions=True)
for result in results:
if isinstance(result, bool) and result:
success_count += 1
# 检查是否被多数节点接受
if success_count > len(self.node.peers) // 2:
# 更新commit_index
self.node.commit_index = new_entry.index
new_entry.is_committed = True
print(f"[Leader {self.node.node_id}] 日志 {new_entry.index} 已提交")
return True
return False
async def _replicate_to_peer(self, peer: RaftNode) -> bool:
"""复制日志到指定节点"""
# 如果next_index表明存在不一致,回退
next_idx = self.node.next_index.get(peer.node_id, 0)
if next_idx > len(self.node.log):
return False
# 构建AppendEntries RPC
prev_log_index = next_idx - 1
prev_log_term = self.node.log[prev_log_index].term if prev_log_index >= 0 else 0
entries = self.node.log[next_idx:]
rpc = AppendEntriesRPC(
term=self.node.current_term,
leader_id=self.node.node_id,
prev_log_index=prev_log_index,
prev_log_term=prev_log_term,
entries=entries,
leader_commit=self.node.commit_index
)
return await self._send_append_entries(peer, empty=False, rpc=rpc)
async def _send_append_entries(
self,
peer: RaftNode,
empty: bool = False,
rpc: Optional[AppendEntriesRPC] = None
) -> bool:
"""发送AppendEntries RPC"""
if not rpc:
rpc = AppendEntriesRPC(
term=self.node.current_term,
leader_id=self.node.node_id,
prev_log_index=self.node.get_last_log_index(),
prev_log_term=self.node.get_last_log_term(),
entries=[],
leader_commit=self.node.commit_index
)
try:
# 模拟网络延迟
await asyncio.sleep(random.uniform(0.01, 0.05))
# 如果term过期,转为follower
if rpc.term > peer.current_term:
peer.current_term = rpc.term
peer.state = NodeState.FOLLOWER
return False
# 如果term较小,拒绝
if rpc.term < peer.current_term:
return False
# 检查日志一致性
if rpc.prev_log_index >= 0:
if (rpc.prev_log_index >= len(peer.log) or
peer.get_log_term(rpc.prev_log_index) != rpc.prev_log_term):
# 日志不一致,返回失败
return False
# 日志一致,添加新条目
if rpc.entries:
# 删除冲突的条目
peer.log = peer.log[:rpc.prev_log_index + 1]
# 添加新条目
peer.log.extend(rpc.entries)
# 更新commit_index
if rpc.leader_commit > peer.commit_index:
peer.commit_index = min(rpc.leader_commit, len(peer.log) - 1)
# 更新Leader的next_index和match_index
if not empty:
last_new_entry = rpc.prev_log_index + len(rpc.entries)
self.node.next_index[peer.node_id] = last_new_entry + 1
self.node.match_index[peer.node_id] = last_new_entry
return True
except Exception as e:
print(f"[Leader {self.node.node_id}] 复制到 {peer.node_id} 失败: {e}")
return False
@dataclass
class AppendEntriesRPC:
"""AppendEntries RPC参数"""
term: int
leader_id: str
prev_log_index: int
prev_log_term: int
entries: List[LogEntry]
leader_commit: int
|