zookeeper入门

zookeeper入门

zookeeper简介及集群搭建

https://zookeeper.apache.org/

4.1. zookeeper概述

分布式应用程序的分布式协调服务

​ Apache ZooKeeper是一项努力开发和维护开源服务器的工作,它能够实现高度可靠的分布式协调。

​ ZooKeeper是一个集中的服务,用于维护配置信息、命名、提供分布式同步以及提供集群服务。

​ Zookeeper 作为 Hadoop 项目中的一个子项目,是 Hadoop 集群管理的一个必不可少的模块,它主要用来控制集群中的数据,如它管理 Hadoop 集群中的 NameNode,还有 Hbase 中 Master Election、Server 之间状态同步等

Zookeeper 会维护一个具有层次关系的数据结构,它非常类似于一个标准的文件系统,如下图所示:

Zookeeper 这种数据结构有如下这些特点:

  1. 每个子目录项如 NameService 都被称作为 znode,这个 znode 是被它所在的路径唯一标识,如 Server1 这个 znode 的标识为 /NameService/Server1
  2. znode 可以有子节点目录,并且每个 znode 可以存储数据,注意 EPHEMERAL 类型的目录节点不能有子节点目录
  3. znode 是有版本的,每个 znode 中存储的数据可以有多个版本,也就是一个访问路径中可以存储多份数据
  4. znode 可以是临时节点,一旦创建这个 znode 的客户端与服务器失去联系,这个 znode 也将自动删除,Zookeeper 的客户端和服务器通信采用长连接方式,每个客户端和服务器通过心跳来保持连接,这个连接状态称为 session,如果 znode 是临时节点,这个 session 失效,znode 也就删除了
  5. znode 的目录名可以自动编号,如 App1 已经存在,再创建的话,将会自动命名为 App2
  6. znode 可以被监控,包括这个目录节点中存储的数据的修改,子节点目录的变化等,一旦变化可以通知设置监控的客户端,这个是 Zookeeper 的核心特性,Zookeeper 的很多功能都是基于这个特性实现的,后面在典型的应用场景中会有实例介绍

Zookeeper 作为一个分布式的服务框架,主要用来解决分布式集群中应用系统的一致性问题,它能提供基于类似于文件系统的目录节点树方式的数据存储,但是 Zookeeper 并不是用来专门存储数据的,它的作用主要是用来维护和监控你存储的数据的状态变化。通过监控这些数据状态的变化,从而可以达到基于数据的集群管理。

4.1.1配置信息的集中式管理(信息的发布和订阅)Configuration Management

​ 应用在启动的时候会主动来获取一次配置,同时,在节点上注册一个Watcher,这样一来,以后每次 配置有更新的时候,都会实时通知到订阅的客户端,从来达到获取最新配置信息的目的。

​ 配置的管理在分布式应用环境中很常见,例如同一个应用系统需要多台 PC Server 运行,但是它们运行的应用系统的某些配置项是相同的,如果要修改这些相同的配置项,那么就必须同时修改每台运行这个应用系统的 PC Server,这样非常麻烦而且容易出错。

​ 像这样的配置信息完全可以交给 Zookeeper 来管理,将配置信息保存在 Zookeeper 的某个目录节点中,然后将所有需要修改的应用机器监控配置信息的状态,一旦配置信息发生变化,每台应用机器就会收到 Zookeeper 的通知,然后从 Zookeeper 获取新的配置信息应用到系统中。

4.1.2 命名服务(Name Service)

​ 通过指定的名字来获取资源或者服务的地址。Zookeeper会在自己的文件系统上(树结构的文件系统)创建一个以路径为名称的节点,它可以指向提供的服务的地址,远程对象等。简单来说使用Zookeeper做命名服务就是用路径作为名字,路径上的数据就是其名字指向的实体。

​ 阿里巴巴集团开源的分布式服务框架Dubbo中使用ZooKeeper来作为其命名服务,维护全局的服务地址列表。在Dubbo实现中:

服务提供者在启动的时候,向ZK上的指定节点/dubbo/$/providers目录下写入自己的URL地址,这个操作就完成了服务的发布。

服务消费者启动的时候,订阅/dubbo//providers目录下的提供者URL地址, 并向/dubbo/ /consumers目录下写入自己的URL地址。

注意,所有向ZK上注册的地址都是临时节点,这样就能够保证服务提供者和消费者能够自动感应资源的变化。

分布式锁(Locks)

​ 共享锁在同一个进程中很容易实现,但是在跨进程或者在不同 Server 之间就不好实现了。Zookeeper 却很容易实现这个功能,实现方式也是需要获得锁的 Server 创建一个 EPHEMERAL_SEQUENTIAL 目录节点,然后调用 [getChildren]方法获取当前的目录节点列表中最小的目录节点是不是就是自己创建的目录节点,如果正是自己创建的,那么它就获得了这个锁,如果不是那么它就调用 exists(String path, boolean watch) 方法并监控 Zookeeper 上目录节点列表的变化,一直到自己创建的节点是列表中最小编号的目录节点,从而获得锁,释放锁很简单,只要删除前面它自己所创建的目录节点就行了。

​ 在一个分布式环境中,为了提高可靠性,我们的集群的每台服务器上都部署着同样的服务。但是,一件事情如果集群中的每个服务器都进行的话,那相互之间就要协调,编程起来将非常复杂。而如果我们只让一个服务进行操作,那又存在单点。通常还有一种做法就是使用分布式锁,在某个时刻只让一个服务去干活,当这台服务出问题的时候锁释放,立即fail over到另外的服务。这在很多分布式系统中都是这么做,这种设计有一个更好听的名字叫Leader Election(leader选举)。

集群管理(Group Membership)

​ Zookeeper 能够很容易的实现集群管理的功能,如有多台 Server 组成一个服务集群,那么必须要一个“总管”知道当前集群中每台机器的服务状态,一旦有机器不能提供服务,集群中其它机器必须知道,从而做出调整重新分配服务策略。同样当增加集群的服务能力时,就会增加一台或多台 Server,同样也必须让“总管”知道。

​ Zookeeper 不仅能够帮你维护当前的集群中机器的服务状态,而且能够帮你选出一个“总管”,让这个总管来管理集群,这就是 Zookeeper 的另一个功能 Leader Election。

​ 它们的实现方式都是在 Zookeeper 上创建一个 EPHEMERAL 类型的目录节点,然后每个 Server 在它们创建目录节点的父目录节点上调用 [getChildren](http://hadoop.apache.org/zookeeper/docs/r3.2.2/api/org/apache/zookeeper/ZooKeeper.html#getChildren(java.lang.String, boolean))(String path, boolean watch) 方法并设置 watch 为 true,由于是 EPHEMERAL 目录节点,当创建它的 Server 死去,这个目录节点也随之被删除,所以 Children 将会变化,这时 [getChildren](http://hadoop.apache.org/zookeeper/docs/r3.2.2/api/org/apache/zookeeper/ZooKeeper.html#getChildren(java.lang.String, boolean))上的 Watch 将会被调用,所以其它 Server 就知道已经有某台 Server 死去了。新增 Server 也是同样的原理。

​ Zookeeper 如何实现 Leader Election,也就是选出一个 Master Server。和前面的一样每台 Server 创建一个 EPHEMERAL 目录节点,不同的是它还是一个 SEQUENTIAL 目录节点,所以它是个 EPHEMERAL_SEQUENTIAL 目录节点。之所以它是 EPHEMERAL_SEQUENTIAL 目录节点,是因为我们可以给每台 Server 编号,我们可以选择当前是最小编号的 Server 为 Master,假如这个最小编号的 Server 死去,由于是 EPHEMERAL 节点,死去的 Server 对应的节点也被删除,所以当前的节点列表中又出现一个最小编号的节点,我们就选择这个节点为当前 Master。这样就实现了动态选择 Master,避免了传统意义上单 Master 容易出现单点故障的问题

前置条件

centos7 安装java

zookeeper单机安装

下载zookeeper

1.新建zookeeper 安装目录,在 /usr/local下新建目录zookeeper

进入新建好的目录zookeeper,再建立目录stand-alone

[root@redis01 local]# mkdir zookeeper
[root@redis01 local]# cd zookeeper/
[root@redis01 zookeeper]# mkdir stand-alone
[root@redis01 zookeeper]# cd stand-alone/
[root@redis01 stand-alone]# pwd
/usr/local/zookeeper/stand-alone

2.解压zookeeper到安装目录/usr/local/zookeeper/stand-alone

[root@redis01 workspace]# tar zxvf zookeeper-3.4.14.tar.gz -C /usr/local/zookeeper/stand-alone/

3.进入解压的安装目录的conf目录,复制zoo_sample.cfg 配置文件 zoo.cfg

[root@redis01 stand-alone]# cd zookeeper-3.4.14/conf/
[root@redis01 conf]# cp zoo_sample.cfg zoo.cfg

在解压目录创建data文件夹

[root@redis01 zookeeper-3.4.14]# pwd
/usr/local/zookeeper/stand-alone/zookeeper-3.4.14
[root@redis01 zookeeper-3.4.14]# mkdir data

修改zoo.cfg如下内容,zookeeper数据存储目录

[root@redis01 zookeeper-3.4.14]# vim conf/zoo.cfg
dataDir=/usr/local/zookeeper/stand-alone/zookeeper-3.4.14/data

4.启动

[root@redis01 zookeeper-3.4.14]# pwd
/usr/local/zookeeper/stand-alone/zookeeper-3.4.14

[root@redis01 zookeeper-3.4.14]# ./bin/zkServer.sh start conf/zoo.cfg 
ZooKeeper JMX enabled by default
Using config: /usr/local/zookeeper/stand-alone/zookeeper-3.4.14/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED

检查进程 zookeepr进程号

5.使用客户端连接服务

[root@redis01 zookeeper-3.4.14]# ./bin/zkCli.sh -server 192.168.66.161:2181

节点类型

节点类型:

1、PERSISTENT--持久化目录节点

​ 客户端与zookeeper断开连接后,该节点依旧存在

2、PERSISTENT_SEQUENTIAL-持久化顺序编号目录节点

​ 客户端与zookeeper断开连接后,该节点依旧存在,只是Zookeeper给该节点名称进行顺序编号。ZooKeeper通过将10位的序列号附加到原始名称来设置znode的路径,顺序节点在锁定和同步中起重要作用。

3、EPHEMERAL-临时目录节点

​ 客户端与zookeeper断开连接后,该节点被删除

4、EPHEMERAL_SEQUENTIAL-临时顺序编号目录节点

​ 客户端与zookeeper断开连接后,该节点被删除,只是Zookeeper给该节点名称进行顺序编号

基本命令

启动服务

./bin/zkServer.sh start conf/zoo.cfg 

客户端连接

[root@redis01 zookeeper-3.4.14]#./bin/zkCli.sh -server 192.168.66.161:2181

help命令查看帮助

close

断开当前连接,但是不退出命令行

[zk: 192.168.13.100:2181(CONNECTED) 10] close  

ls

查看根目录

[zk: 192.168.66.161:2181(CONNECTED) 0] ls /
[zookeeper]

create

创建节点

[zk: 192.168.66.161:2181(CONNECTED) 2] create /user xiaoming
Created /user
[zk: 192.168.66.161:2181(CONNECTED) 3] ls /
[zookeeper, user]

创建带序号的节点 -s

[zk: 192.168.66.161:2181(CONNECTED) 5] create -s /user xiaoming
Created /user0000000001

创建临时节点 -e 命令行退出,结点消失

[zk: 192.168.66.161:2181(CONNECTED) 6] create -e /master 192.168.66.161
Created /master

创建临时的带序号的节点

[zk: 192.168.66.161:2181(CONNECTED) 1] create -e -s /master ""
Created /master0000000003

set

设置节点内的值

[zk: 192.168.66.161:2181(CONNECTED) 5] set /user anne
cZxid = 0x4
ctime = Sat Jan 04 23:39:37 PST 2020
mZxid = 0xc
mtime = Sat Jan 04 23:43:33 PST 2020
pZxid = 0x4
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 4
numChildren = 0

手动设置带上节点版本 dataVersion

如果我们在set的时候手动去指定了版本号,就必须和上一次查询出来的结果一致,否则 就会报错。

这个可以用于我们在修改节点数据的时候,保证我们修改前数据没被别人修改过。因为如果别人修改过了,我们这次修改是不会成功的

[zk: 192.168.66.161:2181(CONNECTED) 8] set /user jason 1
cZxid = 0x4
ctime = Sat Jan 04 23:39:37 PST 2020
mZxid = 0xe
mtime = Sat Jan 04 23:44:24 PST 2020
pZxid = 0x4
cversion = 0
dataVersion = 2
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 5
numChildren = 0

get 路径,查看节点信息

[zk: 192.168.66.161:2181(CONNECTED) 10] get /user
jason
cZxid = 0x4
ctime = Sat Jan 04 23:39:37 PST 2020
mZxid = 0xe
mtime = Sat Jan 04 23:44:24 PST 2020
pZxid = 0x4
cversion = 0
dataVersion = 2
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 5
numChildren = 0

get /aaa watch ,可以监控到结点上的一次事件,只有一次

[zk: 192.168.66.161:2181(CONNECTED) 13] get /user watch
[zk: 192.168.66.161:2181(CONNECTED) 14] set /user anne

WATCHER::

WatchedEvent state:SyncConnected type:NodeDataChanged path:/user
cZxid = 0x4
ctime = Sat Jan 04 23:39:37 PST 2020
mZxid = 0xf
mtime = Sat Jan 04 23:49:03 PST 2020
pZxid = 0x4
cversion = 0
dataVersion = 3
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 4
numChildren = 0

        cZxid:创建节点时的事物ID  
      ctime:创建节点的时间  
      mZxid:节点最新一次更新时的事物ID  
      mtime:最近一次节点更新的时间  
      pZxid:该节点的子节点列表最后一次被修改时的事物ID,如果没有子节点,则为当前节点的cZxid  
      cversion:子节点更新次数  
      dataVersion:节点数据更新次数  
      aclVersion:节点acl更新次数  
      ephemeralOwner:如果节点为ephemeral节点则该值为sessionid,否则为0  
      dataLength:该节点数据的长度  
		numChildren:子节点个数

delete

路径 删除单个节点,路径中不能有子节点,否则,使用 rmr

[zk: 192.168.66.161:2181(CONNECTED) 24] create /user/qy108 anne
[zk: 192.168.66.161:2181(CONNECTED) 27] delete /user
Node not empty: /user

[zk: 192.168.66.161:2181(CONNECTED) 28] rmr /user
[zk: 192.168.66.161:2181(CONNECTED) 29] ls /
[master0000000003, zookeeper, user0000000001]

rmr

路径 删除节点及其子节点

stat

查看节点的状态

[zk: 192.168.66.161:2181(CONNECTED) 1] create -e /user anne
Created /user
[zk: 192.168.66.161:2181(CONNECTED) 2] stat /user

集群搭建(三台虚拟机)

  1. 压缩包解压到指定文件夹

    tar zxvf zookeeper-3.4.14.tar.gz -C /usr/local
    
  2. 第一台服务器的解压包修改为zookeeper1

  3. 新建data文件夹,在文件夹下新建文件myid,写入值1

  4. 修改zoo.vfg文件内容

    #参数说明
    
    tickTime=2000
    #S通信心跳时间Zookeeper 服务器之间或客户端与服务器之间维持心跳的时间间隔也就是每个 		  tickTime 时间就会发送一个心跳。tickTime以毫秒为单位。                     
    initLimit=10 
    #于从节点最初连接到主节点时的初始化时间,单位为tick值的倍数。
    syncLimit=5
    #于主节点与从节点进行同步操作时的超时时间,单位为tick值的倍数。
    dataDir=/tmp/zookeeper
    #用于配置内存数据库保存的模糊快照的目录。即刚刚创建的data文件夹就是在此目录中。文件信息都存放在data目录下。本机数据存放目录
    clientPort=2181
    #示客户端所连接的服务器所监听的端口号,默认是2181。即zookeeper对外提供访问的端口号
    #erver.1本机标识 对应myid里面的文件
    
    # 2888是leader和follower的通信端口
    # 3888是选举投票端口 
    #2888是集群节点的通信端口3888是集群投票端口
    dataDir=/usr/local/zookeeper1/data
    server.1=192.168.66.161:2888:3888
    server.2=192.168.66.162:2888:3888
    server.3=192.168.66.163:2888:3888
    
    
  5. 拷贝当前zookeeper 到server2, server3

    scp -r zookeeper1/ root@192.168.66.163:/usr/local/
    
  6. 修改server2和server3的myid内容,分别改为2和3

  7. 分别启动三台服务器

    ./bin/zkServer.sh start conf/zoo.cfg
    
  8. 查看集群的状态

    ./bin/zkServer.sh status conf/zoo.cfg
    

ZK集群何时不可用

Zookeeper集群中节点个数一般为奇数个2N+1(N>0) 大于等于3,若集群中leader挂掉,剩余follower节点个数在半数以上时,就可以推举新的主节点,继续对外提供服务

集群搭建(伪分布式)

1.在/usr/local/zookeeper 新建 集群文件夹cluster

[root@redis01 zookeeper]# pwd
/usr/local/zookeeper

[root@redis01 zookeeper]# mkdir cluster

2.将 zookeeper 解压到 cluster目录下

[root@redis01 workspace]# tar zxvf zookeeper-3.4.14.tar.gz -C  /usr/local/zookeeper/cluster/

3.在解压的zookeeper-3.4.14 新建data_1 data_2 data_3配置文件

[root@redis01 zookeeper-3.4.14]# pwd
/usr/local/zookeeper/cluster/zookeeper-3.4.14

[root@redis01 zookeeper-3.4.14]# mkdir data-1 data-2 data-3

在data-1 下面执行,将 1 写入myid

[root@redis01 zookeeper-3.4.14]# cd data-1
[root@redis01 data-1]# echo 1 > myid

在data-2 下面执行,将 2 写入myid

[root@redis01 data-1]# cd ../data-2/
[root@redis01 data-2]# echo 2 > myid

在data-3下面执行,将 3 写入myid

echo 3 > myid

4.修改三台server的配置文件

创建第一台server文件
[root@redis01 conf]# pwd
/usr/local/zookeeper/cluster/zookeeper-3.4.14/conf

[root@redis01 conf]# cp zoo_sample.cfg zoo-1.cfg

修改 zoo-1.cfg

# The number of milliseconds of each tick
tickTime=2000
# The number of ticks that the initial 
# synchronization phase can take
initLimit=10
# The number of ticks that can pass between 
# sending a request and getting an acknowledgement
syncLimit=5
# the directory where the snapshot is stored.
# do not use /tmp for storage, /tmp here is just 
# example sakes.
dataDir=/usr/local/zookeeper/cluster/zookeeper-3.4.14/data-1
# the port at which the clients will connect
clientPort=2181
# the maximum number of client connections.
# increase this if you need to handle more clients
#maxClientCnxns=60
#
# Be sure to read the maintenance section of the 
# administrator guide before turning on autopurge.
#
# http://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_maintenance
#
# The number of snapshots to retain in dataDir
#autopurge.snapRetainCount=3
# Purge task interval in hours
# Set to "0" to disable auto purge feature
#autopurge.purgeInterval=1
server.1=192.168.66.161:2888:3888
server.2=192.168.66.161:4888:5888
server.3=192.168.66.161:6888:7888

启动 zoo-1服务

[root@redis01 zookeeper-3.4.14]# ./bin/zkServer.sh start conf/zoo-1.cfg 
ZooKeeper JMX enabled by default
Using config: conf/zoo-1.cfg
Starting zookeeper ... STARTED

[root@redis01 zookeeper-3.4.14]# jps
2676 QuorumPeerMain
2694 Jps

再拷贝2份配置文件

[root@redis01 conf]# pwd
/usr/local/zookeeper/cluster/zookeeper-3.4.14/conf
[root@redis01 conf]# cp zoo-1.cfg zoo-2.cfg 
[root@redis01 conf]# cp zoo-1.cfg zoo-3.cfg 

修改 zoo-2.cfg 注意修改 dataDir clientPort

# The number of milliseconds of each tick
tickTime=2000
initLimit=10
syncLimit=5
# do not use /tmp for storage, /tmp here is just 
# example sakes.
dataDir=/usr/local/zookeeper/cluster/zookeeper-3.4.14/data-2
# the port at which the clients will connect
clientPort=2182
#
# The number of snapshots to retain in dataDir
#autopurge.snapRetainCount=3
# Purge task interval in hours
# Set to "0" to disable auto purge feature
#autopurge.purgeInterval=1
server.1=192.168.66.161:2888:3888
server.2=192.168.66.161:4888:5888
server.3=192.168.66.161:6888:7888

文件保存退出后,启动 zoo-2服务

[root@redis01 zookeeper-3.4.14]# pwd
/usr/local/zookeeper/cluster/zookeeper-3.4.14

[root@redis01 zookeeper-3.4.14]# ./bin/zkServer.sh start conf/zoo-2.cfg 
ZooKeeper JMX enabled by default
Using config: conf/zoo-2.cfg
Starting zookeeper ... STARTED

查看zoo-2 服务状态,zoo-2 变为了leader

[root@redis01 zookeeper-3.4.14]# ./bin/zkServer.sh status conf/zoo-2.cfg 
ZooKeeper JMX enabled by default
Using config: conf/zoo-2.cfg
Mode: leader

配置zoo-3服务

# The number of milliseconds of each tick
tickTime=2000
initLimit=10
syncLimit=5
# do not use /tmp for storage, /tmp here is just 
# example sakes.
dataDir=/usr/local/zookeeper/cluster/zookeeper-3.4.14/data-3
# the port at which the clients will connect
clientPort=2183
#
# The number of snapshots to retain in dataDir
#autopurge.snapRetainCount=3
# Purge task interval in hours
# Set to "0" to disable auto purge feature
#autopurge.purgeInterval=1
server.1=192.168.66.161:2888:3888
server.2=192.168.66.161:4888:5888
server.3=192.168.66.161:6888:7888

启动zoo-3服务

[root@redis01 zookeeper-3.4.14]# ./bin/zkServer.sh start conf/zoo-3.cfg 
ZooKeeper JMX enabled by default
Using config: conf/zoo-3.cfg
Starting zookeeper ... STARTED

[root@redis01 zookeeper-3.4.14]# ./bin/zkServer.sh status conf/zoo-3.cfg 
ZooKeeper JMX enabled by default
Using config: conf/zoo-3.cfg
Mode: follower

使用客户端连接

[root@redis01 zookeeper-3.4.14]# ./bin/zkCli.sh -server 192.168.66.161:2183

Copyright: 采用 知识共享署名4.0 国际许可协议进行许可

Links: https://championmi.cn/archives/zookeeper入门

Buy me a cup of coffee ☕.