本系列是在实现了绝大部分raft论文中描述的功能之后实现过程中遇到的问题,设计的决策等的记录。随着功能的增减,项目的逐渐完善,系统中的实现笔记可能会有偏差,但是基本上对于第一次实现或者想要理解raft的人来说可以作为一个参考。
现在,2018-08-09已经实现的功能
- leader election + log replication
- membership change(one server change)
- log compaction(snapshot)
为了测试简便,现在只实现了memory log,服务器每次重启之后数据会丢失。接下来会实现基于文件的日志。
源代码
https://github.com/xnnyygn/xraft
使用语言和依赖的库
Java 8+(需要Lambda表达式)
库名 | 版本 | 描述 |
---|---|---|
netty | 4.0.36 | 网络通信 |
protobuf | 3.6.0 | 序列化 |
程序分为xraft-core和xraft-kvstore两个模块,后者是基于前者的服务实现,之后可能会增加其他服务。
raft属于分布式一致性算法,本身没有限制服务类型。论文中给出了一个简单的KV store的例子,所以这里就直接用了。
xraft-kvstore提供了服务端和客户端。
服务端启动参数:
usage: xraft-kvstore-server [OPTION]... -gc group config, required when starts with group-member mode. format: ..., format of node-config: ,,, eg: A,localhost,8000 B,localhost,8010 -h host, required when starts with standalone or standby mode -i,--id node id, required. must be unique in group. if starts with mode group-member, please ensure id in group config -m start mode, available: standalone, standby, group-member. default is standalone -p1,--port-raft-server port of raft server, required when starts with standalone or standby mode -p2,--port-service port of service, required
有三种启动模式
- group-member
- standalone
- standby
group-member即以集群成员方式启动,需要指定集群成员(-gc)。特殊的,如果集群成员只有一个(并且是当前)节点,那么和standalone启动方式一致。
standalone为单节点启动,启动后第一次选举timeout之后自动变成leader。standalone方式下日志并不会复制到其他节点,所以添加日志后立马就可以commit。
standby是之后要加入集群的节点,启动后第一次选举timeout时保持follower状态,不会变成candidate。往集群中加入节点时,如果新节点以现有集群配置+自身启动的话,可能会扰乱现有集群,所以xraft中新节点以standby方式启动(不需要指定集群配置),等待来自集群leader的主动连接。
除了服务端的模式之外,服务端还有两个端口,port-raft-server,port-service。也就是常见的内部端口和服务端口。现在新增集群节点和删除集群节点的话是通过服务端口进行的。原因是xraft中内部通信需要交换集群节点id,新增节点的请求端因为不是集群节点,无法指定集群节点id。同样的,删除节点也无法指定当前集群节点id。
kvstore客户端内置一个console,支持以下命令
命令 | 描述 |
---|---|
client-add-server <node-id> <host> <port-service> | client程序增加可用节点 |
client-remove-server <node-id> | client程序删除可用节点 |
client-list-server | 列出当前client程序可用节点 |
client-get-leader | 输出当前client程序使用的leader |
client-set-leader <node-id> | 设置当前client程序使用的leader |
raft-add-server <node-id> <host> <port-raft-server> | 向集群中增加节点 |
raft-remove-server <node-id> | 删除集群节点 |
kvstore-get <key> | kvstore获取key对应的value |
kvstore-set <key> <value> | kvstore设置key对应的key |
client开头的是用于设置当前client的命令。raft开头的是内部处理,比如集群成员增减。kvstore开始的才是服务自身的命令。
usage: xraft-kvstore-client [OPTION]... -gc group config, required. format: . format of server config: ,,. e.g A,localhost,8001 B,localhost,8011
启动后
Welcome to XRaft KVStore Shell *********************************************** current server list: A,localhost,3333 B,localhost,3334 C,localhost,3335 *********************************************** kvstore-client 0.0.1>
本console使用了jline,按两次tab可以列出所有可用命令。
测试用启动流程
- 服务端和客户端console可以分别启动。客户端启动时不会连接服务端
- 假设3节点,以group-member方式启动
- -m group-member -gc A,localhost,2333 B,localhost,2334 C,localhost,2335 -i A -p2 3333
- -m group-member -gc A,localhost,2333 B,localhost,2334 C,localhost,2335 -i B -p2 3334
- -m group-member -gc A,localhost,2333 B,localhost,2334 C,localhost,2335 -i C -p2 3335
- group-member方式启动中可能部分节点访问不到其他节点,可能导致term增加,或者日志中出现持续出现连接失败的消息(可优化)
- 假设3节点,分别是standalone, standby, standby方式启动,客户端在所有节点启动后向standalone发起命令,增加服务器来组成集群
- xraft-kvstore-server -m standalone -i A -p1 2333 -p2 3333
- xraft-kvstore-server -m standby -i B -p1 2334 -p2 3334
- xraft-kvstore-server -m standby -i C -p1 2335 -p2 3335
- xraft-kvstore-cli -gc A,localhost,3333
- kvstore-client> raft-add-server B localhost 2334
- kvstore-client> raft-add-server C localhost 2335
- 测试服务
- xraft-kvstore-cli -gc A,localhost,3333 B,localhost,3334 C,localhost,3335
- kvstore-client> kvstore-get foo
- kvstore-client> kvstore-set foo 1
- kvstore-client> kvstore-get foo
- 如果看到1就说明成功
之后会详细讲解设计与实现。