前言
大家好,我是老马。
分布式系统中,一致性算法是最重要的基石,也是最难学习的部分。
本系列根据 jraft 作为入口,学习一下 raft 的原理和实现。
raft 系列
logcabin
之所以介绍这个库 LogCabin,是因为这个库是Raft作者的开源实现。
概述
LogCabin 是一个分布式系统,提供少量高度复制和一致性的存储。
它是其他分布式系统存储其核心元数据的可靠地方,并在解决集群管理问题时非常有用。
LogCabin 内部使用 Raft 共识算法,并且实际上是 Raft 算法的首个实现。
它以 ISC 许可证 发布(等同于 BSD 许可证)。
外部资源:
有关发布的信息,请查看 RELEASES.md。
本 README 将指导您如何编译和运行 LogCabin。
问题
关于 LogCabin 实现的最佳提问场所是 logcabin-dev 邮件列表。您还可以尝试在 freenode IRC 网络的 #logcabin
频道提问,尽管并不是总有人在线。使用 GitHub Issues 报告问题或建议功能。
如果您对 Raft 共识算法有问题或想讨论,Raft 的实现由 LogCabin 提供,您可以使用 raft-dev 邮件列表。
构建
前提条件:
- Linux x86-64(应支持 v2.6.32 及以上版本)
- git(应支持 v1.7 及以上版本)
- scons(已知 v2.0 和 v2.3 可用)
- g++(已知 v4.4 至 v4.9 和 v5.1 可用)或 clang(已知 v3.4 至 v3.7 可用,且支持 libstdc++ 4.9,libc++ 也支持;更多信息请参见 CLANG.md)
- protobuf(建议使用 v2.6.x,v2.5.x 应该也可用,v2.3.x 不支持)
- crypto++(已知 v5.6.1 可用)
- doxygen(可选;已知 v1.8.8 可用)
简而言之,RHEL/CentOS 6 及更新版本应该适用。
获取源代码:
1
2
3git clone git://github.com/logcabin/logcabin.git
cd logcabin
git submodule update --init
构建客户端库、服务器二进制文件和单元测试:
1scons
对于自定义构建环境,您可以将配置变量放在 Local.sc
文件中。例如,该文件可能如下所示:
1
2BUILDTYPE='DEBUG'
CXXFLAGS=['-Wno-error']
要查看可用的配置参数,请运行:
1scons --help
运行基础测试
在继续之前,建议先运行包含的单元测试:
1build/test/test
您还可以运行一些系统范围的测试。首先运行烟雾测试,这将通过嵌入在 LogCabin 客户端中的内存数据库进行(不涉及服务器):
1build/Examples/SmokeTest --mock && echo 'Smoke test completed successfully'
要在真实的 LogCabin 集群上运行相同的烟雾测试,需要进行更多的设置。
运行真实集群
本节介绍如何在一个三服务器的 LogCabin 集群上运行 HelloWorld
示例程序。我们将暂时在本地主机上运行所有服务器:
- 服务器 1 将监听 127.0.0.1:5254
- 服务器 2 将监听 127.0.0.1:5255
- 服务器 3 将监听 127.0.0.1:5256
端口 5254 是 LogCabin 的默认端口,由 IANA 为 LogCabin 保留。其他两个端口则为其他使用,应该不会在您的网络中冲突。
我们首先需要创建三个配置文件。您可以基于 sample.conf
创建,或者以下内容在当前也有效:
文件 logcabin-1.conf
:
1
2serverId = 1
listenAddresses = 127.0.0.1:5254
文件 logcabin-2.conf
:
1
2serverId = 2
listenAddresses = 127.0.0.1:5255
文件 logcabin-3.conf
:
1
2serverId = 3
listenAddresses = 127.0.0.1:5256
现在您几乎准备好启动服务器。首先,初始化其中一台服务器的日志,配置一个只包含它自己的集群成员配置:
1build/LogCabin --config logcabin-1.conf --bootstrap
ID 为 1 的服务器现在会有一个有效的集群成员配置。此时,集群中只有 1 台服务器,所以只需要 1 票:它可以选举自己为领导者并提交新条目。现在我们可以启动该服务器(保持运行):
1build/LogCabin --config logcabin-1.conf
但是我们不想停在这里,因为只有一台服务器的集群并不具备容错能力!我们将启动另外两台服务器,并将它们添加到第一台服务器的集群中。
首先,在另一个终端中启动第二台服务器(保持运行):
1build/LogCabin --config logcabin-2.conf
请注意,这台服务器只是空闲,等待集群成员配置。它仍然不属于集群。
同样,启动第三台服务器(LogCabin 会检查确保您的新配置中的所有服务器都可用,才会提交切换,避免您做出错误操作):
1build/LogCabin --config logcabin-3.conf
现在使用重新配置命令将第二和第三台服务器添加到集群中:
1
2ALLSERVERS=127.0.0.1:5254,127.0.0.1:5255,127.0.0.1:5256
build/Examples/Reconfigure --cluster=$ALLSERVERS set 127.0.0.1:5254 127.0.0.1:5255 127.0.0.1:5256
该 Reconfigure
命令是一个特殊的 LogCabin 客户端。它首先查询每个在命令行参数中给定的服务器(空格分隔),以获取其服务器 ID 和监听地址。然后,它连接到通过 --cluster
选项指定的集群(逗号分隔),并请求领导者将集群成员设置为这些服务器。注意,如果现有集群成员也要保留在集群中,它们应该包括在命令行参数中,否则它们将被驱逐。
如果成功,您应该看到第一台服务器已将其他服务器添加到集群中,第二和第三台服务器现在也在参与。输出应该类似于:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16Current configuration:
Configuration 1:
- 1: 127.0.0.1:5254
Attempting to change cluster membership to the following:
1: 127.0.0.1:5254 (given as 127.0.0.1:5254)
2: 127.0.0.1:5255 (given as 127.0.0.1:5255)
3: 127.0.0.1:5256 (given as 127.0.0.1:5256)
Membership change result: OK
Current configuration:
Configuration 4:
- 1: 127.0.0.1:5254
- 2: 127.0.0.1:5255
- 3: 127.0.0.1:5256
注意:如果您在所有服务器之间共享一个磁盘,并且负载较重,集群可能会遇到难以保持领导者的问题。有关症状和解决方法,请参见 issue 57。
最后,您可以运行 LogCabin 客户端以测试集群:
1build/Examples/HelloWorld --cluster=$ALLSERVERS
该程序并没有做任何特别有趣的事情。另一个工具 TreeOps
可以在命令行上展示 LogCabin 的数据结构:
1
2echo -n hello | build/Examples/TreeOps --cluster=$ALLSERVERS write /world
build/Examples/TreeOps --cluster=$ALLSERVERS dump
查看 --help
获取完整的可用命令列表
。
您应该能够逐个停止服务器并保持可用性,或者停止多个并重新启动它们并保持安全性(可能会有短暂的可用性中断)。
如果您发现每次都要传递 --cluster=$ALLSERVERS
很麻烦,也可以使用 DNS 名称返回所有 IP 地址。不过,您需要为每个服务器使用不同的 IP 地址,而不仅仅是不同的端口。
如果您有自己的应用程序,您可以将其链接到 build/liblogcabin.a
。您还需要链接以下库:
- pthread
- protobuf
- cryptopp
运行集群范围的测试
如上所述,在运行集群时的过程有点繁琐,特别是当您只是想跑一些测试并立即拆除集群时。为此,scripts/smoketest.py
自动化了这一过程。创建一个名为 scripts/localconfig.py
的文件来覆盖 scripts/config.py
中的 smokehosts
和 hosts
变量:
1
2
3
4
5smokehosts = hosts = [
('192.168.2.1', '192.168.2.1', 1),
('192.168.2.2', '192.168.2.2', 2),
('192.168.2.3', '192.168.2.3', 3),
]
该脚本使用此文件来通过 SSH 启动服务器。每个元组代表一个服务器,包含:
- 用于 SSH 的地址,
- 用于 LogCabin TCP 连接的地址,
- 唯一的 ID。
每台服务器应该能够通过 SSH 无需密码访问,并且 LogCabin 目录应该位于相同的文件系统位置。脚本假设该目录位于共享文件系统中,例如 NFS 挂载或本地磁盘。
您还可以选择创建一个 smoketest.conf
文件,该文件可以定义适用于所有服务器的各种选项。服务器的监听地址将与您的 smoketest.conf
自动合并。
现在,您已经准备好运行:
1scripts/smoketest.py && echo 'Smoke test completed successfully'
此脚本还可以被劫持/包含来运行其他测试程序。
文档
要从源代码构建文档,请运行:
1scons docs
生成的 HTML 文件将放置在 docs/doxygen
目录下。
您也可以在 https://logcabin.github.io 上找到这些文档。
安装
要在文件系统上安装一堆东西,请运行:
1scons install
除了二进制文件外,这还会安装一个与 RHEL 6 兼容的初始化脚本。
如果您不希望这些文件污染您的文件系统,可以将文件安装到任何指定的目录,方法如下(将 pathtoinstallprefix
替换为您希望安装文件的路径):
1scons --install-sandbox=pathtoinstallprefix pathtoinstallprefix
最后,您可以按照以下方式构建一个二进制 RPM:
1scons rpm
这将创建一个名为 build/logcabin-0.0.1-0.1.alpha.0.x86_64.rpm
的文件,您可以使用 RPM 安装,效果与 scons install
相同。
贡献
请使用 GitHub 报告问题和发送拉取请求。
所有提交都应该通过预提交钩子。启用它们以在每次提交之前运行:
1ln -s ../../hooks/pre-commit .git/hooks/pre-commit
参考资料
https://github.com/logcabin/logcabin