前言
我们都知道zookeeper可以作为dubbo的注册中心,但是那只是zookeeper功能的冰山一角。
zookeeper的定位是一个分布式应用程序的分布式协调服务。它提供了一组简单的原语,分布式应用程序可以基于这些原语来实现用于同步(synchronization),配置维护(configuration maintenance)以及组(groups)和命名(naming)的更高级别的服务。
本篇主要从理论和实践两方面讲解zookeeper从单机到集群的相关知识
介绍
zookeeper有三大特点
- 高性能
- 高可用性
- 严格有序访问
zookeeper高性能意味着它可以在大型的分布式系统中使用。高可用性使它不会成为单点故障。严格有序访问意味着可以在客户端上实现复杂的同步原语。
zookeeper提供了类似文件系统的目录树结构样式的数据模型。与文件系统不同的是,zookeeper每一个节点都可以存储数据。最大为1M,数据是二进制安全的,存储数据少是为了保证zookeeper的高性能。zookeeper的数据模型如下
(图片来源:http://zookeeper.apache.org/doc/current/zookeeperOver.html)
zookeeper的节点也叫Znode,分为临时节点和持久节点
- 临时节点:生命周期和
session
相同,session
消失时,zookeeper会自动删除临时节点 - 持久节点:持久化到硬盘,不会随着
session
的消失而被删除
了解了关于zookeeper的基础知识后,就可以开始安装zookeeper了。
实验环境
- VMware Workstation 15
- CentOS Linux release 7.7.1908
- zookeeper-3.4.14
- jdk-8u144-linux-x64.tar.gz
注意事项
- 文章后面集群部分四个节点ip分别为
192.168.1.101
、192.168.1.102
、192.168.1.103
、192.168.1.104
- 确保四个节点都安装了
JDK1.8
,并且配置好了环境变量 - 确保四个节点能够相互通信
- 确保Linux的
wget
、tar
等基础命令可用 - 建议先关闭防火墙,Centos 7操作如下
firewall-cmd --state ## 查看防火墙状态 not running表示已经关闭 systemctl stop firewalld.service ## 关闭防火墙 systemctl disable firewalld.service ## 禁止开机启动防火墙
单机安装
-
下载
wget https://downloads.apache.org/zookeeper/zookeeper-3.4.14/zookeeper-3.4.14.tar.gz
-
解压
tar -zxvf zookeeper-3.4.14.tar.gz
-
配置环境变量
vim /etc/profile
配置内容如下
export ZOOKEEPER_HOME=/root/zookeeper-3.4.14 export PATH=$PATH:$ZOOKEEPER_HOME/bin
刷新配置文件
source /etc/profile
-
修改zookeeper配置文件
cd zookeeper-3.4.14/conf/ ## zookeeper默认加载的是zoo.cfg文件 cp zoo_sample.cfg zoo.cfg ## 创建zookeeper数据存放目录 mkdir /var/lib/zookeeper ## 编辑配置 vim zoo.cfg
zookeeper的配置文件参数比较少,基本上需要修改只有数据目录
# the directory where the snapshot is stored. # do not use /tmp for storage, /tmp here is just # example sakes. dataDir=/var/lib/zookeeper
-
启动
## start 表示后台启动 zkServer.sh start ## start-foreground表示在前台启动 zkServer.sh start-foreground ## 其它启动参数参考命令 zkServer.sh help
-
查看状态
zkServer.sh status
可以查看zookeeper服务的状态[root@localhost ~]# zkServer.sh status ZooKeeper JMX enabled by default Using config: /root/zookeeper-3.4.14/bin/../conf/zoo.cfg Mode: standalone
单节点的安装,很容易就完成了。
操作
zkCli.sh
客户端可以连接zookeeper服务,直接运行即可,默认连接本机的zookeeper服务。连接成功后输入help
可以看到zookeeper服务提供的功能。
[zk: localhost:2181(CONNECTED) 0] help
ZooKeeper -server host:port cmd args
stat path [watch]
set path data [version]
ls path [watch]
delquota [-n|-b] path
ls2 path [watch]
setAcl path acl
setquota -n|-b val path
history
redo cmdno
printwatches on|off
delete path [version]
sync path
listquota path
rmr path
get path [watch]
create [-s] [-e] path data acl
addauth scheme auth
quit
getAcl path
close
connect host:port
命令比较简单,常用的就是create
、rmr
、set
、ls
,也就是增删改查。
需要注意的是create [-s] [-e] path data acl
命令中data
参数是必须的,如果不想写入数据,可以写空字符串""
。
[zk: localhost:2181(CONNECTED) 2] ls /
[zookeeper]
[zk: localhost:2181(CONNECTED) 3] create sicimike
[zk: localhost:2181(CONNECTED) 4] ls /
[zookeeper]
[zk: localhost:2181(CONNECTED) 5] create sicimike ""
Command failed: java.lang.IllegalArgumentException: Path must start with / character
[zk: localhost:2181(CONNECTED) 6] create /sicimike ""
Created /sicimike
[zk: localhost:2181(CONNECTED) 7] ls /
[sicimike, zookeeper]
上述操作可以看到两点
- 如果不加
data
参数是无法创建Znode - 路径必须以
/
开始
[zk: localhost:2181(CONNECTED) 8] create /sicimike/sicimike-1 "hello world"
Created /sicimike/sicimike-1
[zk: localhost:2181(CONNECTED) 9] get /sicimike/sicimike-1
hello world
cZxid = 0x3
ctime = Mon Apr 27 22:33:13 CST 2020
mZxid = 0x3
mtime = Mon Apr 27 22:33:13 CST 2020
pZxid = 0x3
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 11
numChildren = 0
从上述操作可以看出两点
- Znode下面可以继续创建Znode
- Znode中不仅存了命令写入的数据
hello world
,还存放了一些元数据。其中ephemeralOwner
表示临时节点被哪个session
持有。此处为0x0,表示创建的是持久节点。
在用zkCli.sh
连接服务的时候,会有这样一行日志
2020-04-28 20:18:11,225 [myid:] - INFO [main-SendThread(localhost:2181):ClientCnxn$SendThread@1299] - Session establishment complete on server localhost/0:0:0:0:0:0:0:1:2181, sessionid = 0x100005d814d0002, negotiated timeout = 30000
从日志中可以看到,本次连接的sessionid
为0x100005d814d0002
。连接之后,创建一个临时节点
[zk: localhost:2181(CONNECTED) 1] create -e /temp-sicimike "hello world"
Created /temp-sicimike
[zk: localhost:2181(CONNECTED) 2] get /temp-sicimike
hello world
cZxid = 0xe
ctime = Tue Apr 28 20:18:45 CST 2020
mZxid = 0xe
mtime = Tue Apr 28 20:18:45 CST 2020
pZxid = 0xe
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x100005d814d0002
dataLength = 11
numChildren = 0
create -e
表示创建一个临时节点,创建后查看节点,ephemeralOwner
就变成了sessionid
。
客户端退出(命令quit
)时,session
断开,临时节点也会被删除。
单节点的安装和操作都非常简单,接下来进行集群的搭建。
集群搭建
zookeeper的集群是一主多从模式。
(图片来源:http://zookeeper.apache.org/doc/current/zookeeperOver.html)
集群结构如图所示,集群中的节点分为三种角色:leader
、follower
和observer
。
leader
就是主节点,最多只有一个follower
是从节点,可以有多个observer
也是从节点,可以有多个,和follower
唯一的区别就是不参与选举过程。也就是当leader
故障之后,follower
重新选出一个leader
,observer
不会参与这个过程
客户端可以连接任意节点,但是会把写操作交给leader去执行,再由leader把数据同步给所有的follower,以达到数据一致性。
zookeeper服务可以提供一组保证
- 顺序一致性(Sequential Consistency):客户端发送的所有命令都会按顺序执行。
- 原子性(Atomicity ):更新成功或失败。没有部分成功。
- 单个系统映像(Single System Image):无论客户端连接到哪个节点,客户端都将看到相同的服务视图。
- 可靠性(Reliability ):应用更新后,此更新会被持久化。
- 及时性(Timeliness ):确保系统的客户视图在特定时间范围内是最新的。
了解了zookeeper集群相关的知识后,就可以开始搭建zookeeper集群了。
集群的搭建起来非常简单,只需要稍微改写配置文件即可。
修改zoo.cfg
文件,四个节点配置内容相同,均配置如下信息
# The number of milliseconds of each tick
## 每次心跳的超时时间,单位毫秒
tickTime=2000
# The number of ticks that the initial
# synchronization phase can take
## leader和follower之间初始连接的最大心跳次数 initLimit * tickTime 就是最大时间
initLimit=10
# The number of ticks that can pass between
# sending a request and getting an acknowledgement
## leader和follower之间通信的最多能忍受多少次心跳,超时的follower会被踢除集群 syncLimit * tickTime 就是最大时间
syncLimit=5
# the directory where the snapshot is stored.
# do not use /tmp for storage, /tmp here is just
# example sakes.
dataDir=/var/lib/zookeeper
## zookeeper的集群需要手动规划
server.1=192.168.1.101:2888:3888
server.2=192.168.1.102:2888:3888
server.3=192.168.1.103:2888:3888
server.4=192.168.1.104:2888:3888
每个ip配置了2个端口,其中3888
端口的作用是:集群第一次启动尚未选出leader
,或者leader
已经出故障时,节点之间通过3888
端口通信,选出leader
。选出leader
后,其余的节点都会连接leader
的2888
端口进行通信。
记得在每个节点上创建/var/lib/zookeeper
目录,并且在该目录下新建文件,写入自己的服务id
## 节点1 192.168.1.101
echo 1 > myid
## 节点2 192.168.1.103
echo 2 > myid
## 节点3 192.168.1.103
echo 3 > myid
## 节点4 192.168.1.104
echo 4 > myid
配置好了之后,依次启动四个节点。
此时再看节点状态
[root@localhost ~]# zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /root/zookeeper-3.4.14/bin/../conf/zoo.cfg
Mode: leader
[root@localhost ~]# zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /root/zookeeper-3.4.14/bin/../conf/zoo.cfg
Mode: follower
有一个已经变成了leader
,其余的成了follower
。
客户端连接任意一个节点,都是都是可以写入的。其实是follower
节点会把写入命令发送给leader去执行,follower
节点只负责读操作。
之前在查看节点数据的时候,Znode节点中存储数据的格式如下
[zk: localhost:2181(CONNECTED) 6] get /sicimike
hello
cZxid = 0x100000007
ctime = Tue Apr 28 22:03:51 CST 2020
mZxid = 0x100000007
mtime = Tue Apr 28 22:03:51 CST 2020
pZxid = 0x100000007
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 5
numChildren = 0
除了客户端存入的数据hello
,之外,还存储了一些元数据。
cZxid
:是一个64位的二进制数,高32位表示leader的纪元,低32位表示创建的事务ID。因为zookeeper是可以保证顺序一致性的,所以的写操作都由leader来完成,所以写操作会有一个递增的事务IDctime
:Znode创建时间mZxid
:和cZxid
一样,高32位表示leader的纪元,低32位表示修改的事务IDmtime
:Znode修改时间pZxid
:高32位表示leader的纪元,低32位表示当前Znode下,创建最后一个子Znode的事务ID
在创建Znode的时候,可以看到create
命令的结构如下
create [-s] [-e] path data acl
除了可加参数-e
表示临时节点,还可以加参数-s
,操作命令如下
[zk: localhost:2181(CONNECTED) 7] create -s /sicimike "hello world"
Created /sicimike0000000001
[zk: localhost:2181(CONNECTED) 8] create -s /sicimike "hello world 1"
Created /sicimike0000000002
[zk: localhost:2181(CONNECTED) 9] create -s /sicimike "hello world 3"
Created /sicimike0000000003
[zk: localhost:2181(CONNECTED) 10] ls /
[sicimike0000000002, sicimike0000000003, sicimike, zookeeper, sicimike0000000001]
[zk: localhost:2181(CONNECTED) 11] get /sicimike0000000001
hello world
cZxid = 0x100000008
ctime = Tue Apr 28 22:20:27 CST 2020
mZxid = 0x100000008
mtime = Tue Apr 28 22:20:27 CST 2020
pZxid = 0x100000008
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 11
numChildren = 0
可以看到了-s
参数后,创建的Znode都带有数字,并且是一个递增的数字。实际上-s
是sequence
(序列)的意思,就是创建带有序列的Znode。
带序列的Znode可以用于以下场景,当多个客户端想要写入同一个路径(path),而又不想相互覆盖的时候就可以使用带序列的节点。上面的操作中path
都是一样的,但是Znode没有相互覆盖。
前文中说Znode可以分为临时节点和持久节点,这里又可以分成非序列节点和序列节点。根据排列组合可以知道,zookeeper的Znode可以分成四种:临时序列节点、临时非序列节点、持久非序列节点和持久序列节点。
连接状态
前文配置可以看到,集群节点之间通过2888和3888两个端口通信,因此可以通过查看这两个端口的连接状态开观察集群的连接情况。操作命令如下
netstat -natp |egrep ('2888'|'3888')
四个集群节点的连接信息分别如下(博主搭建的集群中,192.168.1.104
是leader)
- 192.168.101
tcp6 0 0 192.168.1.101:3888 :::* LISTEN 1253/java tcp6 0 0 192.168.1.101:3888 192.168.1.102:43972 ESTABLISHED 1253/java tcp6 0 0 192.168.1.101:3888 192.168.1.104:45424 ESTABLISHED 1253/java tcp6 0 0 192.168.1.101:3888 192.168.1.103:56906 ESTABLISHED 1253/java tcp6 0 0 192.168.1.101:59638 192.168.1.104:2888 ESTABLISHED 1253/java
- 192.168.102
tcp6 0 0 192.168.1.102:3888 :::* LISTEN 1179/java tcp6 0 0 192.168.1.102:43972 192.168.1.101:3888 ESTABLISHED 1179/java tcp6 0 0 192.168.1.102:57100 192.168.1.104:2888 ESTABLISHED 1179/java tcp6 0 0 192.168.1.102:3888 192.168.1.103:56180 ESTABLISHED 1179/java tcp6 0 0 192.168.1.102:3888 192.168.1.104:35646 ESTABLISHED 1179/java
- 192.168.103
tcp6 0 0 192.168.1.103:3888 :::* LISTEN 1328/java tcp6 0 0 192.168.1.103:36788 192.168.1.104:2888 ESTABLISHED 1328/java tcp6 0 0 192.168.1.103:56180 192.168.1.102:3888 ESTABLISHED 1328/java tcp6 0 0 192.168.1.103:3888 192.168.1.104:37652 ESTABLISHED 1328/java tcp6 0 0 192.168.1.103:56906 192.168.1.101:3888 ESTABLISHED 1328/java
- 192.168.104
tcp6 0 0 192.168.1.104:2888 :::* LISTEN 1184/java tcp6 0 0 192.168.1.104:3888 :::* LISTEN 1184/java tcp6 0 0 192.168.1.104:2888 192.168.1.103:36788 ESTABLISHED 1184/java tcp6 0 0 192.168.1.104:35646 192.168.1.102:3888 ESTABLISHED 1184/java tcp6 0 0 192.168.1.104:45424 192.168.1.101:3888 ESTABLISHED 1184/java tcp6 0 0 192.168.1.104:37652 192.168.1.103:3888 ESTABLISHED 1184/java tcp6 0 0 192.168.1.104:2888 192.168.1.101:59638 ESTABLISHED 1184/java tcp6 0 0 192.168.1.104:2888 192.168.1.102:57100 ESTABLISHED 1184/java
根据信息可以知道四个节点连接情况如下
绿色的线表示3888端口,也就是用来选出leader的端口,而红色的线是2888端口,是用来leader和follower之间同步指令的端口。
有了连接信息,就能更好的了解zookeeper集群的工作原理。
总结
zookeeper单机和集群的搭建都比较简单,重要的是理解zookeeper的内部结构。对zookeeper有个整体的认识,这将有助于我们更好的使用zookeeper。
参考
- http://zookeeper.apache.org/doc/current/zookeeperOver.html
转载:https://blog.csdn.net/Baisitao_/article/details/105798663