Skip to content

Commit ea6cc70

Browse files
authored
Merge pull request #12 from Icingworld/dev
Perf: refactor channel and raft timer
2 parents 470b4b0 + fffca4b commit ea6cc70

File tree

23 files changed

+3003
-140
lines changed

23 files changed

+3003
-140
lines changed

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
[submodule "third-party/muduo"]
22
path = third-party/muduo
33
url = https://github.com/chenshuo/muduo.git
4+
[submodule "third-party/concurrentqueue"]
5+
path = third-party/concurrentqueue
6+
url = https://github.com/cameron314/concurrentqueue.git

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
cmake_minimum_required(VERSION 3.10)
2-
project(ww-rpc VERSION 2.0.0)
2+
project(ww-rpc VERSION 2.1.0)
33

44
set(CMAKE_CXX_STANDARD 14)
55
set(CMAKE_CXX_STANDARD_REQUIRED ON)

README.md

Lines changed: 62 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,86 +1,101 @@
11
# WW-RaftKV
22

3-
基于Raft共识算法的分布式 KV 存储
3+
基于Raft共识算法的分布式`KV`存储
44

55
## 一、简介
66

7-
该项目是一个基于`Raft`共识算法实现的分布式`KV`存储系统,支持节点容错、高可用部署和一致性日志复制。`WW`系列中学习分布式系统知识后关于`Raft`的简易实现
7+
`WW-RaftKV`是一个基于`Raft`共识算法实现的分布式`KV`存储系统,支持节点容错、高可用部署和一致性日志复制。该项目为`WW`系列的分布式学习项目
88

9-
## 二、依赖
9+
## 二、环境
1010

11-
+ 系统: `Linux` (for `muduo`)
12-
+ 第三方库: `Protobuf3`
11+
+ 系统:64位`Linux`操作系统
12+
+ `C++`版本:`C++14`
13+
+ 第三方库:
14+
+ `Protobuf3`
15+
+ `muduo`
16+
+ `concurrentqueue`
1317

1418
## 三、模块
1519

16-
该实现包含了几个核心模块: `KVStore` `RaftRpc` `Raft`
20+
该实现包含了几个核心模块`KVStore``Logger``Memory-Pool``Raft-Rpc``Raft-Core``Raft-App`
1721

1822
### 1. KVStore
1923

20-
该模块位于`raftkv/kvstore/`,用于提供存分布式存储系统所需要的`KV`存储功能,它的底层实现是跳表。
24+
+ 位于:`raftkv/kvstore/`
25+
+ 功能:是使用跳表实现的`KV`存储,用于提供存分布式存储系统所需要的`KV`存储功能,提供常见的`get``put``update``remove`操作。
2126

22-
### 2. RaftRpc
27+
### 2. Logger
2328

24-
该模块用于提供`Raft`实现中所需要的`Rpc`功能,模块是基于`Google Rpc`框架的扩展。在该框架中,服务端由`Dispatcher``ServiceImpl`组成,并用`Server`封装,`ServiceImpl`提供具体方法实现,`Dispatcher`提供`TcpServer`和服务方法表,处理客户端请求;客户端由`Channel`组成,并用`Client`封装,`Channel`提供具体的`TcpClient`连接实现。
29+
+ 位于:`raftkv/logger`
30+
+ 功能:是一个轻量化的异步日志库,支持自定义格式化输出、同步和异步日志、轮转日志等,用于提供`Raft`运行时必要的日志输出。
2531

26-
#### 服务端
32+
### 3. Memory-Pool
2733

28-
服务端由以下组件组成:
34+
+ 位于:`raftkv/memory-pool`
35+
+ 功能:是一个三级缓存的内存池。它由线程缓存、中心缓存、页缓存三层构成,用于在高频的`Rpc`网络通信中,减小`new`/`delete`动态分配带来的内存碎片和开销。
36+
+ 应用场景:在本项目中,内存池用于`Raft`节点的客户端部分内存管理;服务端部分由于本`Raft`实现的异步设计,请求和响应并不在一个线程中,因此暂不使用内存池,交由`Protubuf``Arena`管理。
2937

30-
+ `ServiceImpl`: 封装`Raft`协议中各类`RPC`方法的具体处理逻辑,包括`RequestVote``AppendEntries``InstallSnapshot`,以及客户端操作`KV`存储所需要的`RaftOperate`方法;
31-
+ `Dispatcher`: 维护服务方法注册表,并内置一个`TcpServer`,用于监听连接并分发请求;
32-
+ `Server`: 对外提供统一的服务端启动接口,整合`Dispatcher``ServiceImpl`
38+
### 4. Raft-Rpc
3339

34-
#### 客户端
40+
+ 位于:`raftkv/raft-rpc`
41+
+ 功能:用于提供`Raft`实现中所需要的`Rpc`功能,该模块是基于`Google Rpc`框架的扩展。
3542

36-
客户端由以下组件组成:
43+
### 5. Raft-Core
3744

38-
+ `Channel`: 封装对某个`Raft`节点的连接与通信逻辑,内部使用`TcpClient`实现持久连接和异步消息发送;
39-
+ `Client`: 管理`Channel`实例,提供面向`Raft`协议调用的高层封装,便于发送`RequestVote``AppendEntries``InstallSnapshot`请求。
45+
+ 位于:`raftkv/raft-core`
46+
+ 功能:实现了`Raft`的核心算法,包括状态机切换、选举、日志复制和推进、心跳等。其核心功能包括:
47+
+ 节点状态管理:支持`Follower``Candidate``Leader`三种角色的状态切换;
48+
+ 选举机制:实现了基于任期的投票逻辑,支持随机超时选举触发与投票限制;
49+
+ 日志复制:`Leader`将客户端请求转化为日志条目并同步给其他节点;
50+
+ 日志一致性保障:通过`prevLogIndex``prevLogTerm`保证日志匹配性;
51+
+ 日志提交与应用:日志在被大多数节点确认后提交,并推送给状态机应用;
52+
+ 心跳机制:`Leader`定期发送心跳保持领导地位并触发空日志复制;
53+
+ 任期与投票状态持久化:防止重启后错误行为;
54+
+ 快照与日志压缩支持:适用于长时间运行场景下的日志膨胀问题。
4055

41-
### 3. Raft
56+
### 6. Raft-App
4257

43-
该模块位于`raftkv/raft-core`,是关于`Raft`的核心算法实现,它采用状态机模式,不关心网络行为,通过上下文与应用层(`RaftClerk`)通信。它负责处理节点间的选举、日志复制、日志提交以及状态同步等关键流程。该模块遵循`Raft`协议的规范设计,具备良好的可读性与可维护性,核心功能包括:
44-
45-
+ 节点状态管理: 支持`Follower``Candidate``Leader`三种角色的状态切换;
46-
+ 选举机制: 实现了基于任期的投票逻辑,支持随机超时选举触发与投票限制;
47-
+ 日志复制: `Leader`将客户端请求转化为日志条目并同步给其他节点;
48-
+ 日志一致性保障: 通过`prevLogIndex``prevLogTerm`保证日志匹配性;
49-
+ 日志提交与应用: 日志在被大多数节点确认后提交,并推送给状态机应用;
50-
+ 心跳机制: `Leader`定期发送心跳保持领导地位并触发空日志复制;
51-
+ 任期与投票状态持久化: 防止重启后错误行为;
52-
+ 快照与日志压缩支持: 适用于长时间运行场景下的日志膨胀问题。
58+
+ 位于:`raftkv/raft-app`
59+
+ 功能:实现了`Raft`的应用层,负责节点间的网络通信,并与`Raft`算法层交互。
5360

5461
## 四、测试
5562

56-
测试代码位于`example/`中,包含了用于模拟启动集群的`raft_example`,使用方法:
63+
测试代码位于`example/`中,包含了以下程序和脚本:
64+
65+
+ `raft_example`:用于模拟启动集群;
66+
+ `raft_client`:用于模拟客户端操作;
67+
+ `run.sh`:启动节点`2-6`
68+
+ `stop.sh`:关闭所有`raft_example`进程;
69+
+ `clean.sh`:清理上一次运行痕迹。
70+
71+
`raft_example`使用方法:
5772

5873
```bash
5974
./raft_example n
6075
```
6176

62-
其中`n`为节点号,该集群中包含`0-8`共9个节点`example/`中还有两个`shell`脚本,`run.sh``stop.sh`,其中,`run.sh`用于启动`2-8`共7个节点,其日志会重定向到`logs/`中;`stop.sh`强制关闭所有`raft_example`进程。
77+
其中`n`为节点号,该集群中包含`0-6`共7个节点`example/`中还有两个`shell`脚本,`run.sh``stop.sh`,其中,`run.sh`用于启动`2-6`共5个节点,其日志会重定向到`logs/`中;`stop.sh`强制关闭所有`raft_example`进程`clean.sh`清理上一次运行产生的痕迹
6378

64-
`example/`中还包含了用于模拟客户端操作请求的`raft_client`使用方法:
79+
`raft_client`使用方法
6580

6681
```bash
6782
./raft_client put a b
6883
./raft_client get a
84+
./raft_client update a c
85+
./raft_client remove a
6986
```
7087

7188
`raft_client`默认连接`node 0`,确保在启动集群时,先启动该节点,再启动剩余节点,以保证`node 0`能够当选`Leader`,当`node 0`不为`Leader`时,`raft_client`请求会返回当前`Leader``IP`地址和端口用于重定向。
7289

73-
当前存储系统支持的操作有:
90+
当前存储系统支持的操作有
7491

75-
+ `put`: 不允许重复的插入键值对,使用方法为`put {key} {value}`
76-
+ `update`: 更新或插入键值对,使用方法为`update {key} {value}`
77-
+ `get`: 读取已存在的键值对,使用方法为`get {key}`
78-
+ `remove`: 删除已存在的键值对,使用方法为`remove {key}`
92+
+ `put`不允许重复的插入键值对,使用方法为`put {key} {value}`
93+
+ `update`更新或插入键值对,使用方法为`update {key} {value}`
94+
+ `get`读取已存在的键值对,使用方法为`get {key}`
95+
+ `remove`删除已存在的键值对,使用方法为`remove {key}`
7996

8097
## 五、快速启动
8198

82-
这是一个快速使用`Raft`测试集群的示例。
83-
8499
### 1. 编译
85100

86101
```bash
@@ -91,51 +106,39 @@ make -j4
91106

92107
### 2. 启动集群
93108

94-
以下按顺序启动
109+
以下需按顺序启动
95110

96-
终端1,启动用于观察`Leader`日志的节点:
111+
+ 窗口1,启动用于观察`Leader`日志的节点:
97112

98113
```bash
99114
cd build/example/
100115
./raft_example 0
101116
```
102117

103-
终端2,启动用于观察`Follower`日志的节点:
118+
+ 窗口2,启动用于观察`Follower`日志的节点
104119

105120
```bash
106121
cd build/example/
107122
./raft_example 1
108123
```
109124

110-
终端3,启动剩余节点:
125+
+ 窗口3,启动剩余节点
111126

112127
```bash
113128
cd build/example/
114129
./run.sh
115130
```
116131

117-
终端4,进行想要的操作:
132+
+ 窗口4,进行客户端操作:
118133

119134
```bash
120135
cd build/example/
121136
./raft_client put hello ww-raftkv
122137
./raft_client get hello
123138
```
124139

125-
### 3. 运行示例
126-
127-
+ node 0
128-
129-
![node 0](doc/img/node%200.png)
130-
131-
+ node 1
132-
133-
![node 1](doc/img/node%201.png)
134-
135-
+ node 2-8
136-
137-
![node 2-8](doc/img/node%202-8.png)
140+
## 六、性能
138141

139-
+ client
142+
使用`perf`工具测试`Leader`节点的性能,测试基于`Debug`模式,`-O2`优化,火焰图如下:
140143

141-
![client](doc/img/client.png)
144+
[flamegraph-raftkv.svg](doc/svg/flamegraph-raftkv.svg)

doc/img/client.png

-13.9 KB
Binary file not shown.

doc/img/node 0.png

-103 KB
Binary file not shown.

doc/img/node 1.png

-60.5 KB
Binary file not shown.

doc/img/node 2-8.png

-31.5 KB
Binary file not shown.

0 commit comments

Comments
 (0)