raft_rust/net/transport.rs
1//! TCP 监听与出站 peer 发送。
2
3// peer 地址表与连接缓存
4use std::collections::HashMap;
5// 按连接缓冲读写
6use std::io::{BufReader, BufWriter};
7// 监听与已连接流
8use std::net::{SocketAddr, TcpListener, TcpStream};
9// 跨线程共享连接表与 writer
10use std::sync::{Arc, Mutex};
11// 每连接一线程处理
12use std::thread;
13// 连接/读写超时
14use std::time::Duration;
15
16// 入站事件投递给 Raft 驱动线程
17use crossbeam::channel::Sender;
18// 连接生命周期日志
19use log::{debug, info, warn};
20// 客户端请求关联 ID
21use uuid::Uuid;
22
23// 帧读写与线路消息
24use super::codec::{read_msg, write_msg, WireMsg};
25// 网络错误映射
26use crate::error::{Error, Result};
27// Raft 信封与客户端请求/响应
28use crate::raft::{Envelope, NodeID, Request, Response};
29
30/// 向已知 peer 发送 Raft 信封(懒连接 + 失败重试一次)。
31// Clone 便于多个发送点共享同一出站连接池
32#[derive(Clone)]
33// 出站邮箱:地址表 + 复用连接池
34pub struct PeerOutbox {
35 // 节点 ID → 静态配置地址(启动时固定)
36 addrs: Arc<HashMap<NodeID, SocketAddr>>,
37 // 节点 ID → 复用的 TCP 连接(写失败后剔除)
38 conns: Arc<Mutex<HashMap<NodeID, TcpStream>>>,
39// PeerOutbox 字段定义结束
40}
41
42// 出站发送与连接管理实现
43impl PeerOutbox {
44 // 用配置中的 peer 列表构造出站邮箱
45 pub fn new(peers: Vec<(NodeID, SocketAddr)>) -> Self {
46 // 初始化地址表与空连接池
47 Self {
48 // 列表转 HashMap,便于 O(1) 查地址
49 addrs: Arc::new(peers.into_iter().collect()),
50 // 初始无连接,首次发送时懒建立
51 conns: Arc::new(Mutex::new(HashMap::new())),
52 // Self 构造结束
53 }
54 // new 结束
55 }
56
57 // 发送一条 Raft 信封;失败则清连接重试一次
58 pub fn send_raft(&self, env: Envelope) -> Result<()> {
59 // 目标节点(信封已带 to)
60 let to = env.to;
61 // 包装为线路层 Raft 消息
62 let msg = WireMsg::Raft(env);
63 // 首次尝试
64 if let Err(e) = self.send_to(to, &msg) {
65 // 断线后清连接再试一次。
66 self.invalidate(to);
67 // 重试仍失败则返回第二次错误
68 if let Err(e2) = self.send_to(to, &msg) {
69 // 再次失败,丢弃坏连接
70 self.invalidate(to);
71 // 向上抛出最终错误
72 return Err(e2);
73 // 二次重试分支结束
74 }
75 // 抑制首次错误的 unused 告警(已重试成功)
76 let _ = e;
77 // 首次失败分支结束
78 }
79 // 首次或重试成功
80 Ok(())
81 // send_raft 结束
82 }
83
84 // 向指定 peer 写一条消息,必要时懒连接
85 fn send_to(&self, id: NodeID, msg: &WireMsg) -> Result<()> {
86 // 查配置地址;未知 ID 视为配置/路由错误
87 let addr = self
88 // 从地址表按节点 ID 查询
89 .addrs
90 // 取引用
91 .get(&id)
92 // 复制 SocketAddr 所有权
93 .copied()
94 // 未配置 peer 则报 IO 路由错误
95 .ok_or_else(|| Error::IO(format!("unknown peer {id}")))?;
96 // 锁定连接表
97 let mut guard = self.conns.lock().expect("lock");
98 // 无缓存连接则建立
99 if !guard.contains_key(&id) {
100 // 按配置地址建立新 TCP
101 let stream = Self::connect(addr)?;
102 // 放入连接池供后续复用
103 guard.insert(id, stream);
104 // 懒连接分支结束
105 }
106 // 取出可变引用写入
107 let stream = guard.get_mut(&id).unwrap();
108 // 写帧;失败则移除连接以便下次重连
109 match write_msg(stream, msg) {
110 // 写成功直接返回
111 Ok(()) => Ok(()),
112 // 写失败进入清理路径
113 Err(e) => {
114 // 写失败时丢掉连接,下次懒重连。
115 guard.remove(&id);
116 // 把原始写错误继续向上抛
117 Err(e)
118 // Err 分支结束
119 }
120 // match write_msg 结束
121 }
122 // send_to 结束
123 }
124
125 // 带超时建立 TCP,并设置 nodelay/读写超时
126 fn connect(addr: SocketAddr) -> Result<TcpStream> {
127 // 2 秒连接超时,避免领导发送路径长时间卡住
128 let stream = TcpStream::connect_timeout(&addr, Duration::from_secs(2))
129 // 连接失败附带目标地址信息
130 .map_err(|e| Error::IO(format!("connect {addr}: {e}")))?;
131 // 关闭 Nagle,降低小包延迟(心跳/投票)
132 stream.set_nodelay(true).ok();
133 // 避免对端卡住时 write 永久阻塞领导发送路径。
134 stream.set_write_timeout(Some(Duration::from_secs(2))).ok();
135 // 读超时:本 outbox 主要写,但设置以免异常路径阻塞
136 stream.set_read_timeout(Some(Duration::from_secs(2))).ok();
137 // 返回已配置超时的流
138 Ok(stream)
139 // connect 结束
140 }
141
142 /// 主动丢弃某 peer 的缓存连接(例如连续发送失败时)。
143 pub fn invalidate(&self, id: NodeID) {
144 // 从连接池移除,下次 send 会重新 connect
145 self.conns.lock().expect("lock").remove(&id);
146 // invalidate 结束
147 }
148// PeerOutbox impl 结束
149}
150
151/// 入站事件:Raft 信封,或客户端请求(需异步回 ClientReply)。
152pub enum Inbound {
153 // 来自 peer 的 Raft 协议消息,交给 Node::step
154 Raft(Envelope),
155 // 来自 CLI/客户端的业务请求;处理完后通过 reply 回写
156 Client {
157 // 与 ClientReply 匹配的请求 ID
158 id: Uuid,
159 // 客户端请求体
160 request: Request,
161 // 单次响应通道,由连接处理线程阻塞等待
162 reply: Sender<std::result::Result<Response, Error>>,
163 // Client 变体字段结束
164 },
165// Inbound 枚举结束
166}
167
168/// 在后台接受连接,把解码后的消息送入 `inbound_tx`。
169pub fn spawn_listener(addr: SocketAddr, inbound_tx: Sender<Inbound>) -> Result<()> {
170 // 绑定监听地址
171 let listener = TcpListener::bind(addr).map_err(|e| Error::IO(format!("bind {addr}: {e}")))?;
172 // 便于运维确认节点已就绪
173 info!("Listening on {addr}");
174 // 独立 accept 线程,避免阻塞 Raft tick 循环
175 thread::spawn(move || {
176 // 持续接受连接
177 for conn in listener.incoming() {
178 // 区分 accept 成功与失败
179 match conn {
180 // 新连接建立成功
181 Ok(stream) => {
182 // 关闭 Nagle
183 stream.set_nodelay(true).ok();
184 // 读超时 30s:空闲连接最终会退出 handle_conn
185 stream.set_read_timeout(Some(Duration::from_secs(30))).ok();
186 // 写超时 2s:避免回写 ClientReply 卡死
187 stream.set_write_timeout(Some(Duration::from_secs(2))).ok();
188 // 克隆 inbound 发送端给连接线程
189 let tx = inbound_tx.clone();
190 // 每连接一线程,简化同步模型
191 thread::spawn(move || handle_conn(stream, tx));
192 // Ok 分支结束
193 }
194 // accept 失败只告警,继续监听
195 Err(e) => warn!("accept error: {e}"),
196 // match conn 结束
197 }
198 // for incoming 结束
199 }
200 // accept 线程闭包结束
201 });
202 // 监听线程已启动,主路径立即返回
203 Ok(())
204// spawn_listener 结束
205}
206
207// 单连接读写循环:Raft 直接转发;Client 同步等待响应再回写
208fn handle_conn(stream: TcpStream, inbound_tx: Sender<Inbound>) {
209 // 读半边用 BufReader
210 let mut reader = BufReader::new(stream.try_clone().expect("clone"));
211 // 写半边共享给可能的超时回写路径
212 let writer = Arc::new(Mutex::new(BufWriter::new(stream)));
213 // 直到读失败/通道关闭
214 loop {
215 // 读一帧
216 let msg = match read_msg(&mut reader) {
217 // 解码成功得到业务消息
218 Ok(m) => m,
219 // 读/解码失败:对端关闭或超时
220 Err(e) => {
221 // 对端关闭或超时
222 debug!("connection closed: {e}");
223 // 退出本连接循环
224 break;
225 // Err 分支结束
226 }
227 // match read_msg 结束
228 };
229 // 按消息类型分发
230 match msg {
231 // Raft 消息:投递给驱动线程
232 WireMsg::Raft(env) => {
233 // 驱动线程已退出则结束连接
234 if inbound_tx.send(Inbound::Raft(env)).is_err() {
235 // 入站通道已关,停止读循环
236 break;
237 // send 失败分支结束
238 }
239 // Raft 分支结束
240 }
241 // 客户端请求:建立 reply 通道并等待处理结果
242 WireMsg::Client { id, request } => {
243 // 容量 1:一次请求一次响应
244 let (reply_tx, reply_rx) = crossbeam::channel::bounded(1);
245 // 投递到 Raft 线程;失败则断连
246 if inbound_tx
247 // 封装为 Inbound::Client 投递给驱动
248 .send(Inbound::Client { id, request, reply: reply_tx })
249 // 通道关闭视为节点关闭
250 .is_err()
251 // if 条件后进入失败体
252 {
253 // 驱动已退出,关闭本连接
254 break;
255 // send 失败分支结束
256 }
257 // 等待 Raft 线程处理完再写回(阻塞本连接线程,简单可靠)。
258 match reply_rx.recv_timeout(Duration::from_secs(30)) {
259 // 正常拿到响应,写 ClientReply
260 Ok(response) => {
261 // 独占写半边,串行回写
262 let mut w = writer.lock().expect("writer");
263 // 写失败则关闭连接
264 if write_msg(&mut *w, &WireMsg::ClientReply { id, response }).is_err() {
265 // 对端写失败,结束连接
266 break;
267 // write 失败分支结束
268 }
269 // Ok 响应分支结束
270 }
271 // 超时:向客户端返回 IO 超时错误
272 Err(_) => {
273 // 超时仍尝试回写错误响应
274 let mut w = writer.lock().expect("writer");
275 // 忽略回写失败(连接可能已断)
276 let _ = write_msg(
277 // 解引用 Mutex 得到可写 BufWriter
278 &mut *w,
279 // 构造超时错误的 ClientReply
280 &WireMsg::ClientReply {
281 // 与请求相同的关联 ID
282 id,
283 // 30s 内未完成(选举/复制慢或领导者不可达)
284 response: Err(Error::IO("request timed out".into())),
285 // ClientReply 构造结束
286 },
287 // write_msg 调用结束
288 );
289 // 超时分支结束
290 }
291 // match recv_timeout 结束
292 }
293 // Client 请求分支结束
294 }
295 // 服务端入站不应出现 ClientReply
296 WireMsg::ClientReply { .. } => {
297 // 服务端不应收到
298 }
299 // match msg 结束
300 }
301 // 连接读循环结束
302 }
303// handle_conn 结束
304}
305
306/// CLI:向单个 peer 发送客户端请求并等待响应。
307pub fn run_client_request(
308 // 目标节点地址
309 addr: SocketAddr,
310 // 业务请求
311 request: Request,
312 // 等待响应超时
313 timeout: Duration,
314// 返回业务响应或网络/协议错误
315) -> Result<Response> {
316 // 建立短连接
317 let stream = TcpStream::connect_timeout(&addr, Duration::from_secs(2))
318 // 连接失败附带地址
319 .map_err(|e| Error::IO(format!("connect {addr}: {e}")))?;
320 // 关闭 Nagle
321 stream.set_nodelay(true).ok();
322 // 读超时使用调用方指定值
323 stream.set_read_timeout(Some(timeout)).ok();
324 // 写超时同样限制
325 stream.set_write_timeout(Some(timeout)).ok();
326 // 可变以便 write_msg/read_msg
327 let mut stream = stream;
328 // 生成本次请求关联 ID
329 let id = Uuid::new_v4();
330 // 发送 Client 帧
331 write_msg(&mut stream, &WireMsg::Client { id, request })?;
332 // 读取并校验响应
333 match read_msg(&mut stream)? {
334 // ID 匹配则解包 Result
335 WireMsg::ClientReply { id: rid, response } if rid == id => response,
336 // 类型或 ID 不符视为协议错误
337 other => Err(Error::InvalidData(format!("unexpected reply: {other:?}"))),
338 // match 响应结束
339 }
340// run_client_request 结束
341}