Zookeeper 是一个分布式调度服务,用来解决分布式系统中的统一调度问题。开发一个分布式系统是一件很困难的事情,主要原因就是因为我们没办法去很有效的处理分布式系统中部分服务失败的问题。在一个分布式系统中,不同机器间是需要互相通信的,并且由通信方发起到被通信方,然后结果要由被通信方回应。这时候假如被通信方因为机房故障或者断电网络等一些因素,没办法通知,这样通信方就不知道发生了什么,这样整个集群就发生了部分失败的故障。Zookeeper 提供了一些工具能够让分布式应用安全合理的处理部分失败的问题。
安装和启动
Zookeeper 的运行有两种方式,一种是 Standalone,还有一个就是 Replicated。这两种的区别就是第一种只部署了单个节点,而且第二种就是以集群的方式来部署。当以集群来部署的时候,客户端不需要连接所有的服务端,只需要连接其中一个就可以获得一致性的读写服务。
下载
到 Apache Zookeeper 的下载页面下载安装包,并且解压出来:
wget https://mirrors.tuna.tsinghua.edu.cn/apache/zookeeper/zookeeper-3.4.11/zookeeper-3.4.11.tar.gz
tar -xzvf zookeeper-3.4.11.tar.gz
cd zookeeper-3.4.11
启动
启动一个 Standalone 的服务很简单,只需要一个配置文件并且放到 conf/zoo.cfg 下面即可,在 conf 目录下 Zookeeper 已经有一个默认的文件了,直接复制过来即可,内容大概是这样:
tickTime=2000
dataDir=/var/lib/zookeeper
clientPort=2181
其中 tickTime 是基本时间单元,以毫秒为单位,用来控制心跳和超时,默认情况下最小的会话超时时间为两倍的 tickTime 。然后启动:
bin/zkServer.sh start
HelloWorld
每个程序的第一步都是跑一个 HelloWorld,Zookeeper 也不例外,输入以下的命令:
echo ruok | nc localhost 2181
你应该会看到:
imok
快速入门
Zookeeper 的数据存储就类似是一个文件系统,但是没有文件和文件夹的概念,取而代之的是以 znode 来标示的。这个 znode 既是数据的容器,也是其他节点的容器,也就是说 znode 可以存储数据和其他 znode (可以理解为文件和文件夹,znode 就是文件夹,里面的数据就是文件,唯一不同的是一个 znode 只能有一个数据,但是可以有多个其他的 znode),可以看一下图:
Zookeeper 自带了一个命令行的客户端,我们可以通过该命令行来操作一下,首先连接到 Zookeeper:
bin/zkCli.sh -server 127.0.0.1:2181
你应该可以看到以下,并且光标一直在闪烁等待输入,可以使用 ls /
命令简单操作一下。
Welcome to ZooKeeper!
[zk: 127.0.0.1:2181(CONNECTED) 0]
在 Zookeeper 中,ZNode 可以分为持久节点和临时节点两类。持久节点是指一旦这个 ZNode 被创建了,除非主动进行删除操作,否则将一直保存在ZooKeeper上;临时节点就是和客户端会话绑定,一旦客户端断开链接,那么这个客户端创建的所有临时节点都会被移除。
创建 znode
[zkshell: 9] create /zk_test my_data
Created /zk_test
其中的/zk_test
是 znode 的名字,my_data
是具体存储的数据,创建成功之后可以用 ls /
来查看一下:
[zkshell: 11] ls /
[zookeeper, zk_test]
获取数据
接下来,通过运行get命令来验证数据是否与znode相关联,如下所示:
[zkshell:12] get / zk_test
my_data
cZxid = 5
ctime = Fri Jun 05 13:57:06 PDT 2009
mZxid = 5
mtime = Fri Jun 05 13:57:06 PDT 2009
pZxid = 5
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0
dataLength = 7
numChildren = 0
更新数据
[zkshell:14] set / zk_test junk
[zkshell:15] get / zk_test
junk
为了方便阅读,去除掉了部分无关的数据显示。
删除数据
接下来删除掉刚才创建的 znode,注意如果该 znode 下有其他的 znode,则无法删除,必须要先把其他的删除了。
[zkshell:16] delete / zk_test
[zkshell:17] ls /
[zookeeper]
集群部署
在开发和测试环境中我们可以以 Standalone 的方式来运行,但是如果要部署到生产环境那么就应用以 Replicated 的方式来部署以获得更高的可用性。在该模式下至少需要三台服务器,并且强烈建议要有一个奇数的服务器。如果你只有两台服务器,那么就会有这样一种情况,如果其中一台服务器出现故障,就会没有足够的机器来形成大多数投票决策,两台服务器本质上不如 一台服务器稳定,因为有两个单点故障。修改配置文件:
tickTime = 2000
dataDir = /var/lib/zookeeper
clientPort = 2181
initLimit = 5
syncLimit = 2
server.1 = zoo1:2888:3888
server.2 = zoo2:2888:3888
server.3 = zoo3:2888:3888
如果有三台机器的话,每台机器都要这样的配置。然后还要到/var/lib/zookeeper
新建一个myid
的文本文件,内容就是server.X
中的X
。接下来就可以按照正常方式启动了,启动之后 Zookeeper 会自动投票选举出一个 Leader,其他的机器就是 Follower
ACL 权限
在 Linux 的文件系统中,权限是以组和用户来表示的,在 Zookeeper 有他自己的一套 ACL 权限用来控制客户端的访问权限(类似用户对资源是否有读写权限),通过鉴权来获得客户端的身份(类似账号密码登录)。它本身的 ACL 机制是以 schemepermissions来表示的,第一个字段表示采用哪种机制,第二个 id 表示用户,permissions表示相关权限(如只读,读写,管理等)。Zookeeper 总共提供了五中的 scheme :
- world: 下面只有一个 id 叫做 anyone,Zookeeper 中开放所有权限的结点就是 world:anyone
- auth: 它不需要id, 只要是通过认证的用户都有权限
- digest: 它对应的 id 为 username:BASE64(SHA1(password)),它需要先通过 username:password 形式的认证
- ip: 它对应的 id 为客户机的 IP 地址,并且可以设置成一个 IP 段比如 ip:192.168.1.0/16
- super: 在这种scheme下,对应的 id 拥有超级权限
只是 ACL 毕竟是简单的访问控制,并非完整的权限管理,还是有很多局限性。比如 ACL 没有继承机制,所有的 ZNode 创建后都需要重新设置 ACL 而无法继承上一级的。
基本实例
在对 ZooKeeper 有了一个简单的了解以后,我们就可以用 Zookeeper 来构建一个简单的配置管理服务。集群中的机器可以通过 ZooKeeper 共享一个通用的配置数据,并且可以提供检索和更新配置的服务,我们会用 PHP 语言来实现。
安装扩展
PHP 在操作 Zookeeper 的时候是需要安装一个扩展的,我们可以到 PECL 上面去下载。要注意的是该扩展要依赖一个 C 语言编写的客户端,Zookeeper 已经内置好了,进去 zookeeper-3.4.11/src/c
的目录下,编译安装即可。
配置服务
接下来我们要新建一个 /config
的节点,把所有的配置都放到该节点下,很简单:
[zkshell: 9] create /config my_data
Created /config
我们设计用 ZNode 来存储配置的 key-value 键值对,我们在 ZNode 中存储一个 String 类型的数据作为 value,用 ZNode 的 path 来表示 key。定义一个客户端来读取数据,并且通过 CLI 的方式运行起来,代码不能再简单了:
$zc = new Zookeeper();
$zc->connect('localhost:2181');
$path = '/config/key1';
$zc->get($path, function($i, $type, $key) use ($zc, $path){
echo $key."\n";
echo $zc->get($path);
});
while(true){
echo ".\n";
sleep(1);
}
然后通过 Zookeeper 自带的命令行客户端来修改一下配置:
切换回去 PHP CLI 的控制台终端窗口应该就可以看到:
我们所实现的程序目前都是假设网络稳定的情况下实现的,但是一个真实的情况是,网络环境是会有很多原因导致不稳定的。这里不会讲述如何正确的处理这些失败,会在接下来的高级篇里面在阐述。
欢迎关我的个人公众号:左手代码
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。