目录
syntax="proto2";
package example;
option cc_generic_services = true;
message BlockRequest {
required int64 offset = 1;
optional int32 size = 2;
}
message BlockResponse {
required bool success = 1;
optional string redirect = 2;
}
service BlockService {
rpc write(BlockRequest) returns(BlockResponse);
rpc read(BlockRequest) returns(BlockResponse);
};
server
类关系
classDiagram
class Block {
-Node* _node
-int64_t _leader_term
-SharedFD* _fd
+start()
+write()
+read()
+shutdown()
+join()
}
class BlockServiceImpl {
-Block* _block
+write()
+read()
}
class BlockClosure {
-Block* _block
-BlockRequest* _request
-BlockResponse* _response
-butil::IOBuf* _data
-google::protobuf::Closure* _done
+BlockClosure(Block*, BlockRequest*, BlockResponse*, IOBuf*, Closure*)
+~BlockClosure()
+const BlockRequest* request()
+BlockResponse* response()
+butil::IOBuf* data()
+void Run()
}
class braft_Closure {
<<interface>>
+Run()*
}
BlockClosure --|> braft_Closure
BlockServiceImpl --> Block
BlockClosure --> Blockgraph TD
A[main函数] --> B[创建Block实例]
B --> C[创建BlockServiceImpl]
C --> D[启动BRPC Server]
D --> E[启动Block服务]
E --> F[等待退出信号]
F --> G[关闭服务]Block类
syntax="proto2";
package example;
option cc_generic_services = true;
message BlockRequest {
required int64 offset = 1;
optional int32 size = 2;
}
message BlockResponse {
required bool success = 1;
optional string redirect = 2;
}
service BlockService {
rpc write(BlockRequest) returns(BlockResponse);
rpc read(BlockRequest) returns(BlockResponse);
};
主要作用
graph TD
A[Block类] --> B[状态机管理]
A --> C[数据读写]
A --> D[快照管理]
B --> B1[Leader选举]
B --> B2[状态同步]
C --> C1[写操作]
C --> C2[读操作]
D --> D1[保存快照]
D --> D2[加载快照]
uml类图
classDiagram
class StateMachine {
<<interface>>
+on_apply(Iterator& iter)*
+on_shutdown()
+on_snapshot_save(SnapshotWriter*, Closure*)
+on_snapshot_load(SnapshotReader*)
+on_leader_start(int64_t term)
+on_leader_stop(Status&)
+on_error(Error&)
+on_configuration_committed(Configuration&)
+on_stop_following(LeaderChangeContext&)
+on_start_following(LeaderChangeContext&)
}
class Block {
-butil::Mutex _fd_mutex
-braft::Node* volatile _node
-butil::atomic<int64_t> _leader_term
-scoped_fd _fd
+Block()
+~Block()
+int start()
+void write(BlockRequest*, BlockResponse*, IOBuf*, Closure*)
+void read(BlockRequest*, BlockResponse*, IOBuf*)
+bool is_leader()
+void shutdown()
+void join()
-void redirect(BlockResponse*)
#void on_apply(Iterator&)
#void on_snapshot_save(SnapshotWriter*, Closure*)
#int on_snapshot_load(SnapshotReader*)
#void on_leader_start(int64_t)
#void on_leader_stop(Status&)
#void on_shutdown()
#void on_error(Error&)
#void on_configuration_committed(Configuration&)
#void on_stop_following(LeaderChangeContext&)
#void on_start_following(LeaderChangeContext&)
}
class SharedFD {
-int _fd
+explicit SharedFD(int fd)
+int fd()
-~SharedFD()
}
class SnapshotArg {
+scoped_fd fd
+SnapshotWriter* writer
+Closure* done
}
Block --|> StateMachine
Block *-- SharedFD
Block *-- SnapshotArg
主要功能流程
写入流程:
sequenceDiagram Client->>BlockServiceImpl: write请求 BlockServiceImpl->>Block: 转发请求 Block->>RAFT: 提交日志 RAFT-->>Block: 应用日志 Block->>Storage: 写入数据 Block-->>Client: 返回结果sequenceDiagram Client->>BlockServiceImpl: read请求 BlockServiceImpl->>Block: 转发请求 Block->>Block: 检查是否Leader Block->>Storage: 读取数据 Block-->>Client: 返回数据BlockClosure 类
这个类在整个系统中扮演着重要的桥接角色,它将异步的RAFT操作与RPC请求响应关联起来,并确保资源的正确释放。
主要职责
生命周期
sequenceDiagram participant Client participant Block participant BlockClosure participant RAFT Client->>Block: 发起写请求 Block->>BlockClosure: 创建Closure Block->>RAFT: 提交任务 RAFT-->>BlockClosure: 调用Run() BlockClosure-->>Client: 返回结果 Note over BlockClosure: 自动销毁要点
- 一致性保证:
- 写操作必须通过RAFT日志复制
- 读操作只在Leader节点处理
- 支持Leader切换和重定向
- 快照机制:
- 定期保存状态机快照
- 支持快照恢复
- 通过硬链接优化快照存储
- 错误处理:
- 支持请求重定向
- 磁盘错误处理
- Leader切换通知
graph LR A[配置项] --> B[端口] A --> C[选举超时] A --> D[快照间隔] A --> E[数据目录] A --> F[集群配置]tricks
expected_term避免ABA问题
- 请求只能在发起时的term中被处理
- 如果节点经历了Leader身份变化,即使term值相同,请求也会被拒绝
- 客户端可以重试请求,确保在正确的Leader上处理
这样就有效避免了ABA问题带来的数据一致性风险。
让我解释一下RAFT中的ABA问题以及为什么设置expected_term可以避免它:
sequenceDiagram participant C as Client participant N1 as Node1(Term=1) participant N2 as Node2 participant N3 as Node3 Note over N1: 初始Leader C->>N1: 写请求(term=1) Note over N1: 失去Leader地位 Note over N2: 成为Leader(term=2) Note over N1: 重新成为Leader(term=3) Note over N1: 继续处理之前的请求不设置expected_term时的ABA问题:
sequenceDiagram participant Client participant Node as Node(Leader) Note over Node: Term=1 (A状态) Client->>Node: 发送写请求 Note over Node: Term变为2 (B状态) Note over Node: Term重新变为1 (A状态) Note over Node: 继续处理旧请求 Note right of Node: 危险!请求可能在错误的term被处理设置expected_term后:
sequenceDiagram participant Client participant Node as Node(Leader) Note over Node: Term=1 Client->>Node: 发送写请求(expected_term=1) Note over Node: Term变为2 Note over Node: Term重新变为1 Note over Node: 检查expected_term Node-->>Client: 拒绝请求(term不匹配)解释:
- 什么是ABA问题
- A: 节点是Leader,term=1
- B: 节点失去Leader地位,term变化
- A: 节点重新成为Leader,term又变回来
- 表面上看term相同,但实际上节点经历了状态变化
- 可能导致旧请求在新的term中被错误处理
- 违反RAFT的安全性保证
- 可能导致数据不一致
- expected_term如何解决
- 记录发送请求时的term
- 处理请求时检查当前term是否与expected_term相同
- 即使term值相同,只要中间发生过变化,term的历史就不同
- 通过比较确保请求只能在原始term中处理
内存序参数使用
graph TD A[内存序类型] --> B[Relaxed] A --> C[Acquire] A --> D[Release] B --> B1[最宽松的内存序] B --> B2[只保证原子性] B --> B3[不提供同步] C --> C1[读取操作使用] C --> C2[保证后续读写不会重排到acquire之前] D --> D1[写入操作使用] D --> D2[保证之前的读写不会重排到release之后]memory_order_relaxed的使用
const int64_t term = _leader_term.load(butil::memory_order_relaxed);在write()函数中使用
memory_order_acquire的使用
bool is_leader() const { return _leader_term.load(butil::memory_order_acquire) > 0; }
- 在检查leader状态时使用
- 确保读取到leader状态后,能看到之前所有的写入
- 防止后续操作重排到这个读取之前
void on_leader_start(int64_t term) { _leader_term.store(term, butil::memory_order_release); LOG(INFO) << "Node becomes leader"; }
- 在更改leader状态时使用
- 确保之前的所有写入对其他线程可见
- 防止之前的操作重排到这个写入之后
同步关系示意:
sequenceDiagram participant T1 as Thread1 participant M as Memory participant T2 as Thread2 Note over T1,T2: Release-Acquire 配对 T1->>M: store(term, release) Note over T1: 所有之前的写入 Note over T1: 不能重排到此后 M->>T2: load(acquire) Note over T2: 所有后续的读写 Note over T2: 不能重排到此前使用这些内存序的原因:
1. 正确性保证
graph LR A[Leader变更] --> B[Release写入] B --> C[Acquire读取] C --> D[后续操作] E[普通检查] --> F[Relaxed读取]
- 性能考虑
graph TD A[内存序强度] --> B[性能开销] B --> C[Relaxed<Release/Acquire<Seq_cst]
No comments yet.