An C++ implementation of RAFT consensus algorithm based on jrpc
该版本的raft实现源自MIT 6.824,做完lab以后我尝试用C++实现一遍。相比于go语言的版本,这个版本的特点有:
一个raft节点有两个线程(即两个EventLoop),一个跑rpc server,另一个跑raft算法以及rpc client。若将这两部分放在一个线程里面固然可以简化代码(单线程编程),但是由于rpc框架调度的延迟不确定,可能导致心跳发送不及时。也许应该把rpc client单独放在一个线程,在jrpc的支持下这点不难做到。
核心部分是raft的纯算法实现(Raft.h/Raft.cc),它的rpc请求、回复以及时钟都需要外部输入,这些输入具体包括:
Raft::RequestVote()
,Raft::AppendEntries()
)Raft::OnRequestVoteReply()
, Raft::OnAppendEntriesReply()
)Raft::Tick()
)Raft::Propose()
)我并没有将rpc请求和回复关联起来,而是当成独立的消息输入来处理,这样方便处理 expired/duplicate/out-of-order rpc 消息。用户并不直接使用Raft类,而是使用Node类(Node.h/Node.cc)。Node类封装了rpc通信、时钟、多线程等内容。
首先,你需要gcc 7.x或者更高的版本,可以手动安装或者直接上Ubuntu 18.04。。。
sudo apt install libleveldb-dev
git clone https://github.com/guangqianpeng/raft.git
cd raft
git submodule update --init --recursive
./build.sh
./build.sh install
cd ../raft-build/Release-install/bin
开一个单节点的raft,server端口号是9877
,虽然这个server没什么用:
./raft_demo 0 9877 | grep 'raft\['
你可以看到运行的流程,每隔1s,leader就会propose一条log,最后一行是每隔5s输出一次的统计信息:
20180504 07:06:39.926993 raft[0] follower, peerNum = 1 starting...
20180504 07:06:40.427085 raft[0] follower -> candidate
20180504 07:06:40.427127 raft[0] candidate -> leader
20180504 07:06:40.927118 raft[0] leader, term 1, start log 1
20180504 07:06:40.927164 raft[0] leader, term 1, apply log [1]
20180504 07:06:41.927097 raft[0] leader, term 1, start log 2
20180504 07:06:41.927145 raft[0] leader, term 1, apply log [2]
20180504 07:06:42.927097 raft[0] leader, term 1, start log 3
20180504 07:06:42.927144 raft[0] leader, term 1, apply log [3]
20180504 07:06:43.927027 raft[0] leader, term 1, start log 4
20180504 07:06:43.927082 raft[0] leader, term 1, apply log [4]
20180504 07:06:44.927069 raft[0] leader, term 1, #votes 1, commit 4
开3个节点的raft集群,需要起3个进程,server端口号分别是9877,9878,9879
分别运行:
./raft_demo 0 9877 9878 9879 | grep 'raft\['
./raft_demo 1 9877 9878 9879 | grep 'raft\['
./raft_demo 2 9877 9878 9879 | grep 'raft\['
你可以看到leader election的过程,然后重启一个进程,看看会发生什么?Have fun :-)