目录

一、Zookeeper分布式协调中间件快速入门

1. 什么是zookeeper

通过官网介绍:“ZooKeeper是一种集中式服务,可维护配置信息,命名,提供分布式同步和提供组服务。”

2. 涉及的应用

  1. Apache Dubbo
  2. Apache Kafka
  3. Spring Cloud Zookeeper
  4. Neo4j
  5. Apache Hbase
  6. Apache Hive
  7. Apache Spark
  8. Apache Flume
  9. Apache Solr
  10. Apache Hadoop
  11. Apache Hadoop Reduce
  12. 京东,淘宝,阿里,美团自研中间件

3. Zookeeper核心概念

数据模型

alt

Zookeeper维护一个类似文件系统的数据模型
每个子目录项都被称作为 node(目录节点),和文件系统类似,我们能够自由的增加、删除 node,在一个node下增加、删除子znode。
有6 种类型的znode:(3.5.x版本以前只有前面四种)

4. 节点类型
类型 描述
PERSISTENT 持久节点
PERSISTENT_SEQUENTIAL 持久序号节点
EPHEMERAL 临时节点(不可在拥有子节点)
EPHEMERAL_SEQUENTIAL 临时序号节点(不可在拥有子节点)
Container 3.5.3 版本新增,当前节点下没有子节点此节点会被zk清除 默认60s检查一次
TTL 3.5.3 版本新增,默认禁用,只能通过系统配置 zookeeper.extendedTypesEnabled=true 开启,不稳定

5. 事件监听机制

客户端注册监听它关心的任意节点,或者目录节点及递归子目录节点

  1. 如果注册的是对某个节点的监听,则当这个节点被删除,或者被修改时,对应的客户端将被 通知
  2. 如果注册的是对某个目录的监听,则当这个目录有子节点被创建,或者有子节点被删除,对 应的客户端将被通知
  3. 如果注册的是对某个目录的递归子节点进行监听,则当这个目录下面的任意子节点有目录结 构的变化(有子节点被创建,或被删除)或者根节点有数据变化时,对应的客户端将被通知。

注意:所有的通知都是一次性的,及无论是对节点还是对目录进行的监听,一旦触发,对应的监听即被移除。递归子节点,监听是对所有子节点的,所以,每个子节点下面的事件同样只会被触 发一次。

下面安装zk服务,安装步骤参考这篇博客安装zk3.4.9服务。这里实际操作使用3.5.9版本.

6. Zookeeper客户端实操

进入zk客户端

bin/zkCli.sh -server 127.0.0.1 #server后面代表连接指定地址

输入命令 help 查看zookeeper所支持的所有命令

创建zookeeper 节点命令

create [-s] [-e] [-c] [-t ttl] path [data] [acl]

中括号为可选项,没有则默认创建持久化节点 -s: 顺序节点

-e: 临时节点

-c: 容器节点

-t: 可以给节点添加过期时间,默认禁用,需要通过系统参数启用 (-Dzookeeper.extendedTypesEnabled=true, znode.container.checkIntervalMs : (Java system property only) New in 3.5.1: The time interval in milliseconds for each check of candidate container and ttl nodes. Default is “60000”.)

创建节点:

create /node-test some-data 如上,没有加任何可选参数,创建的就是持久化节点
alt
查看节点数据: get /node-test
alt
修改节点数据
set /node-test second-modify
alt

查看节点状态信息 stat /node-test
alt

1
2
3
4
5
6
7
8
9
10
11
12
Stat: 字段含义

cZxid:创建znode的事务ID(Zxid的值)。
mZxid:最后修改znode的事务ID。 pZxid:最后添加或删除子节点的事务ID(子节点列表发生变化才会发生改变)。
ctime:znode创建时间。
mtime:znode最近修改时间。
dataVersion:znode的当前数据版本。
cversion:znode的子节点结果集版本(一个节点的子节点增加、删除都会影响这个版本)。
aclVersion:表示对此znode的acl版本。
ephemeralOwner:znode是临时znode时,表示znode所有者的session ID。 如果znode不是 临时znode,则该字段设置为零。
dataLength:znode数据字段的⻓度。
numChildren:znode的子znode的数量。

根据状态数据中的版本号有并发修改数据实现乐观锁的功能 比如: 客户端首先获取版本信息,get -s /node-test
alt
/node-test 当前的数据版本是 1 , 这时客户端 用 set 命令修改数据的时候可以把版本号带上
alt
如果在执行上面 set命令前, 有人修改了数据,zookeeper 会递增版本号, 这个时候,如果再 用以前的版本号去修改,将会导致修改失败,报如下错误
alt
创建子节点, 这里要注意,zookeeper是以节点组织数据的,没有相对路径这么一说,所以, 所有的节点一定是以 / 开头。

create /node-test/test-sub-node
alt

查看子节点信息,比如根节点下面的所有子节点, 加一个大写 R 可以查看递归子节点列表 ls / 创建临时节点
create -e /ephemeral data

create 后跟一个 -e 创建临时节点 , 临时节点不能创建子节点
alt
创建序号节点,加参数 -s

create /seq-parent data // 创建父目录,单纯为了分类,非必须 create -s /seq-parent/ data // 创建顺序节点。顺序节点将再seq-parent 目录下面,顺序递增

为了容纳子节点,先创建个父目录 /seq-parent
alt
也可以再序号节点前面带一个前缀
alt
创建临时顺序节点,其它增删查改和其他节点无异, create -s -e /ephemeral-node

注意:临时节点由于不能创建子节,点所以创建顺序节点无前缀
创建容器节点

create -c /container 容器节点主要用来容纳字节点,如果没有给其创建子节点,容器节点表现和持久化节点一样,如 果给容器节点创建了子节点,后续又把子节点清空,容器节点也会被zookeeper删除。

事件监听

针对节点的监听:一定事件触发,对应的注册立刻被移除,所以事件监听是一次性的

get -w /path // 注册监听的同时获取数据

stat -w /path // 对节点进行监听,且获取元数据信息

alt
针对目录的监听,如下图,目录的变化,会触发事件,且一旦触发,对应的监听也会被移除,后 续对节点的创建没有触发监听事件
ls -w /path
alt
针对递归子目录的监听

ls -R -w /path : -R 区分大小写,一定用大写

如下对/test 节点进行递归监听,但是每个目录下的目录监听也是一次性的,如第一次在/test
目录下创建节点时,触发监听事件,第二次则没有,同样,因为时递归的目录监听,所以
在/test/sub0下进行节点创建时,触发事件,但是再次创建/test/sub0/subsub1节点时,没有 触发事件。

Zookeeper事件类型一览:

1
2
3
4
5
6
7
None: 连接建立事件
NodeCreated: 节点创建
NodeDeleted: 节点删除
NodeDataChanged:节点数据变化
NodeChildrenChanged:子节点列表变化
DataWatchRemoved:节点监听被移除
ChildWatchRemoved:子节点监听被移除

7. Zookeeper客户端使用

由于zookeeper客户端和服务端源码是绑定在一起的,因为之前在实际项目使用过程中遇到过坑,建议使用和服务端代码相同版本的客户端

1
2
3
4
5
6
<!-- 引入 maven -->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.5.9</version>
</dependency>

init 方法被执行后,就可以用zookeeper实例进行通过调用api操作,主要代码如下:

1
2
3
4
5
6
7
8
9
10
11
ZooKeeper zooKeeper=null;
public void initailZkServer() throws IOException, InterruptedException {
final CountDownLatch countDownLatch = new CountDownLatch(1);
zooKeeper = new ZooKeeper("127.0.0.1:2181", 5000, event -> {
if (event.getState() == Watcher.Event.KeeperState.SyncConnected && event.getType() == Watcher.Event.EventType.None) {
countDownLatch.countDown();
System.out.println("连接成功!");
}
});
System.out.println("连接中...");
countDownLatch.await();

8. 事务日志

针对每一次客户端的事务操作,Zookeeper都会将他们记录到事务日志中,当然,Zookeeper也 会将数据变更应用到内存数据库中。我们可以在zookeeper的主配置文件zoo.cfg 中配置内存中 的数据持久化目录,也就是事务日志的存储路径 dataLogDir. 如果没有配置dataLogDir(非必 填), 事务日志将存储到dataDir (必填项)目录。

zookeeper提供了格式化工具可以进行数据查看事务日志数据 org.apache.zookeeper.server.LogFormatter

1
2
java -cp :/Users/m_yf/soft/zookeeper-3.5.9/lib/slf4j-api-1.7.25.jar:/Users/m_yf/soft/zookeeper-3.5.9/lib/zookeeper-3.5.9.jar:/Users/m_yf/soft/zookeeper-3.5.9/lib/zookeeper-jute-3.5.9.jar org.apache.zookeeper.server.LogFormatter /Users/m_yf/soft/zookeeper-3.5.9/logs/version-2/log.1

生成结果如下:
alt

从左到右分别记录了操作时间,客户端会话ID,CXID,ZXID,操作类型,节点路径,节点数据(用 #+ascii 码表示),节点版本。 Zookeeper进行事务日志文件操作的时候会频繁进行磁盘IO操作,事务日志的不断追加写操作会 触发底层磁盘IO为文件开辟新的磁盘块,即磁盘Seek。因此,为了提升磁盘IO的效率, Zookeeper在创建事务日志文件的时候就进行文件空间的预分配- 即在创建文件的时候,就向操 作系统申请一块大一点的磁盘块。这个预分配的磁盘大小可以通过系统参数 zookeeper.preAllocSize 进行配置。

事务日志文件名为: log.<当时最大事务ID>,应为日志文件时顺序写入的,所以这个最大事务 ID也将是整个事务日志文件中,最小的事务ID,日志满了即进行下一次事务日志文件的创建

9. 数据快照

数据快照用于记录Zookeeper服务器上某一时刻的全量数据,并将其写入到指定的磁盘文件中。 可以通过配置snapCount配置每间隔事务请求个数,生成快照,数据存储在dataDir 指定的目录 中,
可以通过如下方式进行查看快照数据( 为了避免集群中所有机器在同一时间进行快照,实际的 快照生成时机为事务数达到 [snapCount/2 + 随机数(随机数范围为1 ~ snapCount/2 )] 个数 时开始快照)

1
java -cp :/Users/m_yf/soft/zookeeper-3.5.9/lib/slf4j-api-1.7.25.jar:/Users/m_yf/soft/zookeeper-3.5.9/lib/zookeeper-3.5.9.jar:/Users/m_yf/soft/zookeeper-3.5.9/lib/zookeeper-jute-3.5.9.jar org.apache.zookeeper.server.SnapshotFormatter /Users/m_yf/soft/zookeeper-3.5.9/data/version-2/snapshot.0

生成结果如下:
alt

快照事务日志文件名为: snapshot.<当时最大事务ID>,日志满了即进行下一次事务日志文件的 创建 有了事务日志,为啥还要快照数据。

快照数据主要时为了快速恢复,事务日志文件是每次事务请求都会进行追加的操作,而快照是达 到某种设定条件下的内存全量数据。所以通常快照数据是反应当时内存数据的状态。事务日志是 更全面的数据,所以恢复数据的时候,可以先恢复快照数据,再通过增量恢复事务日志中的数据 即可。

二、Zookeeper客户端使用与集群特性

1. Zookeeper ACL权限控制

Zookeeper 的ACL 权限控制,可以控制节点的读写操作,保证数据的安全性,Zookeeper ACL 权 限设置分为 3 部分组成,分别是:权限模式(Scheme)、授权对象(ID)、权限信息 (Permission)。最终组成一条例如“scheme:id:permission”格式的 ACL 请求信息。

下面我们具体看一下这 3 部分代表什么意思:

权限模式(Scheme)

用来设置 ZooKeeper 服务器进行权限验证的方式。ZooKeeper 的权限验证方式大体分为三种 类型:

一种是范围验证。所谓的范围验证就是说 ZooKeeper 可以针对一个 IP 或者一段 IP 地址授予某 种权限。比如我们可以让一个 IP 地址为“ip:192.168.0.110”的机器对服务器上的某个数据节点 具有写入的权限。或者也可以通过“ip:192.168.0.1/24”给一段 IP 地址的机器赋权。在 ZooKeeper 中这种验证方式是 IP认证。

另一种权限模式就是口令验证,也可以理解为用户名密码的方式。在 ZooKeeper 中这种验证方 式是 digest 认证,而 digest 这种认证方式首先在客户端传送“username:password”这种形式 的权限表示符后,ZooKeeper 服务端会对密码 部分使用 SHA-1 和 BASE64 算法进行加密, 以保证安全性。

还有一种Super权限模式(超级管理员), Super可以认为是一种特殊的 Digest 认证。具有 Super 权限的客户端可以对 ZooKeeper 上的任意数据节点进行任意操作。

授权对象(ID)

授权对象就是说我们要把权限赋予谁,而对应于不同的权限模式来说,如果我们选择采用 IP 方 式,使用的授权对象可以是一个 IP 地址或 IP 地址段;而如果使用 Digest 或 Super 方式,则 对应于一个用户名。如果是 World ,则是授权系统中所有的用户。

权限信息(Permission)

权限就是指我们可以在数据节点上执行的操作种类(CRUD),如下所示:在 ZooKeeper 中已 经定义好的权限有 5 种:

1
2
3
4
5
6
1. 数据节点(c: create)创建权限,授予权限的对象可以在数据节点下创建子节点;
2. 数据节点(w: wirte)更新权限,授予权限的对象可以更新该数据节点;
3. 数据节点(r: read)读取权限,授予权限的对象可以读取该节点的内容以及子节点的列表信
息;
4. 数据节点(d: delete)删除权限,授予权限的对象可以删除该数据节点的子节点;
5. 数据节点(a: admin)管理者权限,授予权限的对象可以对该数据节点体进行 ACL 权限设置。
权限控制命令:
1
2
3
4
1. getAcl:获取某个节点的acl权限信息
2. setAcl:设置某个节点的acl权限信息
3. addauth: 输入认证授权信息,相当于注册用户信息,注册时输入明文密码,zk将以密文的形式存储
禁用权限控制: 可以通过系统参数zookeeper.skipACL=yes进行配置,默认是no,可以配置为true, 则配置过的 ACL将不再进行权限检测
授权Id生成

Zookeeper 静态类 DigestAuthenticationProvider 提供了静态方法 generateDigest 可以生成 授权ID

1
2
String sid= DigestAuthenticationProvider.generateDigest("user1:pwd1");
sid=user1:a9l5yfb9zl8WCXjVmi5/XOC0Ep4=

设置ACL API 有两种方式

  • 节点创建的同时设置ACL 或者 单独设置 ACL 信息
  • create [-s] [-e] [-c] path [data] [acl] 或者 setAcl /path [acl]

口令验证

我们先用digest (口令验证)进行设置, digest 又有明文和密文两种方式,我们先用密文进行 设置

1
create /node1 data digest:user1:a9l5yfb9zl8WCXjVmi5/XOC0Ep4=:c

上面的命令中 [user1:a9l5yfb9zl8WCXjVmi5/XOC0Ep4=] 为授权对象

如果node1 已经存在,则可以通过 setAcl 进行设置

1
setAcl /node1 digest:user1:a9l5yfb9zl8WCXjVmi5/XOC0Ep4=:c

添加授权信息后,不能直接访问,直接访问将报如下异常

访问前需要添加授权信息

1
addauth digest user1:pwd1

另一种授权模式: auth 明文授权 使用之前需要先

addauth digest username:password 注册用户信息,后续可以直接用明文授权

1
2
3
addauth digest user2:pwd2
create /node2 node2data auth:user2:pwd2:cdwra
这时 user2 用户授权信息会被zk保存,可以认为当前的授权用户为user2 get /node2 node2data
IP授权模式:
1
2
setAcl /node-ip ip:192.168.109.128:cdwra
create /node-ip data ip:192.168.109.128:cdwra 多个指定IP可以通过逗号分隔, 如 setAcl /node-ip ip:IP1:rw,ip:IP2:a
Super 超级管理员模式

这是一种特殊的Digest模式, 在Super模式下超级管理员用户可以对Zookeeper上的节点进行任何的操作。需要通过JVM 系统参数开启:

1
-Dzookeeper.DigestAuthenticationProvider.superDigest=super1:hB2CGqPXvJ/YuuIOhzfjc0rnt+s=

配置位置在zkServer.sh文件下
alt

2. Zookeeper Java客户端

项目构建

ZooKeeper 官方的客户端没有和服务端代码分离,他们为同一个jar 文件,所以我们直接引入 ZooKeeper的maven即可, 这里版本请保持与服务端版本一致,不然会有很多兼容性的问题

引入依赖

1
2
3
4
5
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.5.9</version>
</dependency>
ZooKeeper 构造函数核心参数
参数名称
含义
connectString ZooKeeper服务器列表,由英文逗号分开的 host:port字符串组成, 每一个都代表一台ZooKeeper机器,如, host1:port1,host2:port2,host3:port3。另 外,也可以在connectString中设置客户端连 接上ZooKeeper 后的根目录,方法是在host:port字符串之后 添加上这个根目录,例 如,host1:port1,host2:port2,host3:port3/zk- base,这样就指定了该客户端连接上 ZooKeeper服务器之后,所有对ZooKeeper 的操作,都会基于这个根目录。例如,客户端 对/sub-node 的操作,最终创建 /zk- node/sub-node, 这个目录也叫Chroot,即客 户端隔离命名空间。
sessionTimeout 会话的超时时间,是一个以“毫秒”为单位的整 型值。在ZooKeeper中有会话的概念,在一个会话周期内,ZooKeeper客户端和服务器之间会通过心跳检 测机制来维持会话的有效性,一旦在 sessionTimeout时间内没有进行有效 的心跳检测,会话就会失效。
watcher ZooKeeper允许 客户端在构造方法中传入一个接口 watcher (org.apache. zookeeper. Watcher)的实现类对象来作为默认的 Watcher事件通知处理器。当然,该参 数可以设置为null 以表明不需要设置默认的 Watcher处理器。
canBeReadOnly 这是一个boolean类型的参数,用于标识当前 会话是否支持“read-only(只 读)”模式。默认情况下,在ZooKeeper集群 中,一个机器如果和集群中过半及 以上机器失去了网络连接,那么这个机器将不 再处理客户端请求(包括读写请 求)。但是在某些使用场景下,当ZooKeeper 服务器发生此类故障的时候,我们 还是希望ZooKeeper服务器能够提供读服务 (当然写服务肯定无法提供)——这就是 ZooKeeper的“read-only”模式。
sessionId和 ses sionPasswd 分别代表会话ID和会话秘钥。这两个参数能够 唯一确定一个会话,同时客户端使用这两个参 数可以实现客户端会话复用,从而达到恢复会 话的效果。具体使用方法是,第一次连接上 ZooKeeper服务器时,通过调用ZooKeeper 对象实例的以下两个接口,即可获得当前会话 的ID和秘钥:
long getSessionId(); byte[] getSessionPasswd( ); 荻取到这两个参数值之后,就可以在下次创建 ZooKeeper对象实例的时候传
入构造方法了
代码示例

同步创建持久化节点:

1
zooKeeper.create("/ZK_NODE1", "data".getBytes(),ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);

异步创建节点:

1
2
zooKeeper.create("/ZK_NODE2", "data".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, (rc, path, ctx, name) ->
log.info("rc {},path {},ctx {},name {}", rc, path, ctx, name), "context");

3. Apache Curator 开源客户端

什么是 Curator

Curator 是一套由netflix 公司开源的,Java 语言编程的 ZooKeeper 客户端框架,Curator项目 是现在ZooKeeper 客户端中使用最多,对ZooKeeper 版本支持最好的第三方客户端,并推荐使 用,Curator 把我们平时常用的很多 ZooKeeper 服务开发功能做了封装,例如 Leader 选举、 分布式计数器、分布式锁。这就减少了技术人员在使用 ZooKeeper 时的大部分底层细节开发工 作。在会话重新连接、Watch 反复注册、多种异常处理等使用场景中,用原生的 ZooKeeper 处理比较复杂。而在使用 Curator 时,由于其对这些功能都做了高度的封装,使用起来更加简 单,不但减少了开发时间,而且增强了程序的可靠性。

Curator 实战

这里我们以 Maven 工程为例,首先要引入Curator 框架相关的开发包,这里为了方便测试引入 了junit ,lombok,由于ZooKeeper本身以来了 log4j 日志框架,所以这里可以创建对应的 log4j配置文件后直接使用。 如下面的代码所示,我们通过将 Curator 相关的引用包配置到 Maven 工程的 pom 文件中,将 Curaotr 框架引用到工程项目里,在配置文件中分别引用了两 个 Curator 相关的包,第一个是 curator-framework 包,该包是对 ZooKeeper 底层 API 的一 些封装。另一个是 curator-recipes 包,该包封装了一些 ZooKeeper 服务的高级特性,如: Cache 事件监听、选举、分布式锁、分布式 Barrier。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<!-- 引入依赖-->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-x-discovery</artifactId>
<version>5.0.0</version>
<exclusions>
<exclusion>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>5.0.0</version>
<exclusions>
<exclusion>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.5.9</version>
</dependency>
会话创建

要进行客户端服务器交互,第一步就要创建会话
Curator 提供了多种方式创建会话,比如用静态工厂方式创建:

1
2
3
4
//重试策略
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3)
CuratorFramework client = CuratorFrameworkFactory.newClient(zookeeperConnectionString, retryPolicy);
client.start();

或者使用 fluent ⻛格创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
RetryPolicy retryPolicy = new ExponentialBackoffRetry(5000, 30);

curatorFramework = CuratorFrameworkFactory.builder()
.connectString(zkHosts)
.retryPolicy(retryPolicy)
.sessionTimeoutMs(sessionTimeoutMs)
.connectionTimeoutMs(connectionTimeoutMs)
.canBeReadOnly(true) // 集群环境
.build();
curatorFramework.getConnectionStateListenable().addListener((client, newState) -> {
if (newState == ConnectionState.CONNECTED) {
log.info("连接成功!");
}

});
log.info("连接中......");
curatorFramework.start();

这段代码的编码⻛格采用了流式方式,最核心的类是 CuratorFramework 类,该类的作用是定 义一个 ZooKeeper 客户端对象,并在之后的上下文中使用。在定义 CuratorFramework 对象 实例的时候,我们使用了 CuratorFrameworkFactory 工厂方法,并指定了 connectionString 服务器地址列表、retryPolicy 重试策略 、sessionTimeoutMs 会话超时时间、 connectionTimeoutMs 会话创建超时时间。下面我们分别对这几个参数进行讲解:

connectionString:服务器地址列表,在指定服务器地址列表的时候可以是一个地址,也可以是 多个地址。如果是多个地址,那么每个服务器地址列表用逗号分隔, 如 host1:port1,host2:port2,host3;port3 。

retryPolicy:重试策略,当客户端异常退出或者与服务端失去连接的时候,可以通过设置客户端 重新连接 ZooKeeper 服务端。而 Curator 提供了 一次重试、多次重试等不同种类的实现方 式。在 Curator 内部,可以通过判断服务器返回的 keeperException 的状态代码来判断是否进行重试处理,如果返回的是 OK 表示一切操作都没有问题,而 SYSTEMERROR 表示系统或服 务端错误。

策略名称 描述
ExponentialBackoffRetry 重试一组次数,重试之间的睡眠时间增加
RetryNTimes 重试最大次数
RetryOneTime 只重试一次
RetryUntilElapsed 在给定的时间结束之前重试
超时时间:Curator 客户端创建过程中,有两个超时时间的设置。一个是 sessionTimeoutMs 会话超时时间,用来设置该条会话在 ZooKeeper 服务端的失效时间。另一个是 connectionTimeoutMs 客户端创建会话的超时时间,用来限制客户端发起一个会话连接到接收 ZooKeeper 服务端应答的时间。sessionTimeoutMs 作用在服务端,而 connectionTimeoutMs 作用在客户端。

4. Zookeeper集群特性

ZooKeeper 集群⻆色

Zookeeper 集群模式一共有三种类型的⻆色

Leader: 处理所有的事务请求(写请求),可以处理读请求,集群中只能有一个Leader
Follower: 只能处理读请求,同时作为Leader的候选节点,即如果Leader宕机,Follower节点要参与到新的Leader选举中,有可能成为新的Leader节点。
Observer: 只能处理读请求。不能参与选举

alt

Zookeeper 集群模式安装

本次实例为伪集群搭建,真正集群模式无非在多个物理上运行实例
1.data目录下创建数据文件并写入myid (集群唯一标识)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
cd ./data 
mkdir ./zk-1 ./zk-2 ./zk-3 ./zk-4
#写入myid作为集群唯一标识
echo 1 >zk1/myid
echo 2 >zk2/myid
echo 3 >zk3/myid
echo 4 >zk4/myid

#设置配置文件
cd ./conf
cp ./zoo_sample.cfg zoo1.cfg
#vim 修配置文件
dataDir=/Users/m_yf/soft/zookeeper-3.5.9/data/zk1
clientPort=2181
server.1=127.0.0.1:2001:3001:participant // participant 可以不用写,默认就是participant server.2=127.0.0.1:2002:3002:participant
server.3=127.0.0.1:2003:3003:participant
server.4=127.0.0.1:2004:3004:observer

配置说明

  • tickTime:用于配置Zookeeper中最小时间单位的长度,很多运行时的时间间隔都是使用 tickTime的倍数来表示的。
  • initLimit:该参数用于配置Leader服务器等待Follower启动,并完成数据同步的时间。 Follower服务器再启动过程中,会与Leader建立连接并完成数据的同步,从而确定自己对 外提供服务的起始状态。Leader服务器允许Follower再initLimit 时间内完成这个工作。
  • syncLimit:Leader 与Follower心跳检测的最大延时时间
  • dataDir:顾名思义就是 Zookeeper 保存数据的目录,默认情况下,Zookeeper 将写数据
    的日志文件也保存在这个目录里。
  • clientPort:这个端口就是客户端连接 Zookeeper 服务器的端口,Zookeeper 会监听这个
    端口,接受客户端的访问请求。
  • server.A=B:C:D:E 其中 A 是一个数字,表示这个是第几号服务器;B 是这个服务器的
    ip 地址;C 表示的是这个服务器与集群中的 Leader 服务器交换信息的端口;D 表示的是万 一集群中的 Leader 服务器挂了,需要一个端口来重新进行选举,选出一个新的 Leader, 而这个端口就是用来执行选举时服务器相互通信的端口。如果是伪集群的配置方式,由于 B 都是一样,所以不同的 Zookeeper 实例通信端口号不能一样,所以要给它们分配不同的端 口号。如果需要通过添加不参与集群选举以及事务请求的过半机制的 Observer节点,可以 在E的位置,添加observer标识。

启动集群

1
2
3
4
bin/zkServer.sh start conf/zoo1.cfg 
bin/zkServer.sh start conf/zoo2.cfg
bin/zkServer.sh start conf/zoo3.cfg
bin/zkServer.sh start conf/zoo4.cfg

检查集群状态

1
2
3
bin/zkCli.sh-serverip1:port1,ip2:port2,ip3:port3,ip4:port4
或者
zkServer.sh status conf/zoo1.cfg
ZooKeeper 动态扩容/缩容

ZooKeeper 3.5.0 以前,ZooKeeper集群⻆色要发生改变的话,只能通过停掉所有的 ZooKeeper服务,修改集群配置,重启服务来完成,这样集群服务将有一段不可用的状态,为 了应对高可用需求,ZooKeeper 3.5.0 提供了支持动态扩容/缩容的 新特性。但是通过客户端 API可以变更服务端集群状态是件很危险的事情,所以在ZooKeeper 3.5.3 版本要用动态配置, 需要开启超级管理员身份验证模式 ACLs。如果是在一个安全的环境也可以通过配置 系统参数 -Dzookeeper.skipACL=yes 来避免配置维护acl 权限配置。

  1. 第一步, 按照上面的演示,先配置超级管理员账户,或者 配置跳过ACL权限控制 (zookeeper.skipACL=yes)

  2. 创建文件 zoo_replicated1.cfg.dynamic 动态配置文件,加入集群信息
    server.A=B.C.D.E;F

    1
    2
    3
    4
    5
    6
    7
    A: 服务的唯一标识 
    B: 服务对应的IP地址
    C: 集群通信端口
    D: 集群选举端口
    E: ⻆色, 默认是 participant,即参与过半机制的⻆色,选举,事务请求过半提交,还有 一个是observer, 观察者,不参与选举以及过半机制。
    之后是一个分号,一定是分号
    F:服务IP:端口
  3. 修改配置文件:
    修改配置 zoo1.cfg
    注意这里去除了端口号,添加了
    reconfigEnabled=true : 设置为true 开启动态配置 dynamicConfigFile=/指定路径 : 指定动态配置文件的路径

zoo_replicated1.cfg.dynamic配置文件示例:

1
2
3
4
server.1=127.0.0.1:2001:3001:participant;127.0.0.1:2181
server.2=127.0.0.1:2002:3002:participant;127.0.0.1:2182
server.3=127.0.0.1:2003:3003:participant;127.0.0.1:2183
server.4=127.0.0.1:2004:3004:observer;127.0.0.1:2184
  1. 依次配置其他服务 zoo2.cfg ,zoo3.cfg ······ , 注意数据文件的路径,依次启动所有服 务
  2. 查看集群配置信息 连上任意一台服务器:
    执行config 命令, 查看 集群配置信息 也可以 get /zookeeper/config 进行查看
  3. 修改集群配置
    如果要修改集群状态 需要先以管理员授权登录
    1
    2
    3
    4
    5
    addauth digest super1:pwd1
    # 移除serverId为 3 的机器
    reconfig -remove 3
    # 把对应的机器加进来
    reconfig -add server.3=127.0.0.1:2003:3003:participant;127.0.0.1:2183
    如果要变更/或者添加新的服务需要将服务加到配置文件 zoo_replicated1.cfg.dynamic 中,启动服务,然后通过reconfig 命令进行添加或者变更服务⻆色,但是需要保证服务列表中 participant ⻆色能够形成集群(过半机制) 客户端可以通过监听 /zookeeper/confg 节点,来感知集群的变化。从而实现集群的动态变更. Zookeeper类提供了对应的API用来更新服务列表 : updateServerList