小言_互联网的博客

zookeeper从入门到放弃

419人阅读  评论(0)

前言

我们都知道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.101192.168.1.102192.168.1.103192.168.1.104
  • 确保四个节点都安装了JDK1.8,并且配置好了环境变量
  • 确保四个节点能够相互通信
  • 确保Linux的wgettar等基础命令可用
  • 建议先关闭防火墙,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

命令比较简单,常用的就是creatermrsetls,也就是增删改查。

需要注意的是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

从日志中可以看到,本次连接的sessionid0x100005d814d0002。连接之后,创建一个临时节点

[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)

集群结构如图所示,集群中的节点分为三种角色:leaderfollowerobserver

  • leader就是主节点,最多只有一个
  • follower是从节点,可以有多个
  • observer也是从节点,可以有多个,和follower唯一的区别就是不参与选举过程。也就是当leader故障之后,follower重新选出一个leaderobserver不会参与这个过程

客户端可以连接任意节点,但是会把写操作交给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后,其余的节点都会连接leader2888端口进行通信。

记得在每个节点上创建/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来完成,所以写操作会有一个递增的事务ID
  • ctime:Znode创建时间
  • mZxid:和cZxid一样,高32位表示leader的纪元,低32位表示修改的事务ID
  • mtime: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都带有数字,并且是一个递增的数字。实际上-ssequence(序列)的意思,就是创建带有序列的Znode

带序列的Znode可以用于以下场景,当多个客户端想要写入同一个路径(path),而又不想相互覆盖的时候就可以使用带序列的节点。上面的操作中path都是一样的,但是Znode没有相互覆盖。

前文中说Znode可以分为临时节点持久节点,这里又可以分成非序列节点序列节点。根据排列组合可以知道,zookeeper的Znode可以分成四种:临时序列节点临时非序列节点持久非序列节点持久序列节点

连接状态

前文配置可以看到,集群节点之间通过2888和3888两个端口通信,因此可以通过查看这两个端口的连接状态开观察集群的连接情况。操作命令如下

netstat -natp |egrep ('2888'|'3888')

四个集群节点的连接信息分别如下(博主搭建的集群中,192.168.1.104leader

  • 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
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场