1
0
mirror of https://github.com/Snailclimb/JavaGuide synced 2025-06-20 22:17:09 +08:00

[refractor]zookeeper部分文章重构完善

This commit is contained in:
guide 2020-08-05 18:44:34 +08:00
parent 98ad66183c
commit 7fd6cedc8b
6 changed files with 683 additions and 415 deletions

View File

@ -321,9 +321,9 @@ SSO(Single Sign On)即单点登录说的是用户登陆多个子系统的其中
> 前两篇文章可能有内容重合部分,推荐都看一遍。 > 前两篇文章可能有内容重合部分,推荐都看一遍。
1. [【入门】ZooKeeper 相关概念总结](docs/system-design/framework/ZooKeeper.md) 1. [【入门】ZooKeeper 相关概念总结 01](docs/system-design/framework/zookeeper-intro.md)
2. [【进阶】Zookeeper 原理简单入门!](docs/system-design/framework/ZooKeeper-plus.md) 2. [【进阶】ZooKeeper 相关概念总结 02](docs/system-design/framework/zookeeper-plus.md)
3. [拓展】ZooKeeper 数据模型和常见命令](docs/system-design/framework/ZooKeeper数据模型和常见命令.md) 3. [实战】ZooKeeper 实战](docs/system-design/framework/zookeeper-in-action.md)
#### 其他 #### 其他

View File

@ -1,8 +1,41 @@
[FrancisQ](https://juejin.im/user/5c33853851882525ea106810) 投稿。 [FrancisQ](https://juejin.im/user/5c33853851882525ea106810) 投稿。
# ZooKeeper
## 好久不见 <!-- @import "[TOC]" {cmd="toc" depthFrom=1 depthTo=6 orderedList=false} -->
<!-- code_chunk_output -->
- [1. 好久不见](#1-好久不见)
- [2. 什么是ZooKeeper](#2-什么是zookeeper)
- [3. 一致性问题](#3-一致性问题)
- [4. 一致性协议和算法](#4-一致性协议和算法)
- [4.1. 2PC两阶段提交](#41-2pc两阶段提交)
- [4.2. 3PC三阶段提交](#42-3pc三阶段提交)
- [4.3. `Paxos` 算法](#43-paxos-算法)
- [4.3.1. prepare 阶段](#431-prepare-阶段)
- [4.3.2. accept 阶段](#432-accept-阶段)
- [4.3.3. `paxos` 算法的死循环问题](#433-paxos-算法的死循环问题)
- [5. 引出 `ZAB`](#5-引出-zab)
- [5.1. `Zookeeper` 架构](#51-zookeeper-架构)
- [5.2. `ZAB` 中的三个角色](#52-zab-中的三个角色)
- [5.3. 消息广播模式](#53-消息广播模式)
- [5.4. 崩溃恢复模式](#54-崩溃恢复模式)
- [6. Zookeeper的几个理论知识](#6-zookeeper的几个理论知识)
- [6.1. 数据模型](#61-数据模型)
- [6.2. 会话](#62-会话)
- [6.3. ACL](#63-acl)
- [6.4. Watcher机制](#64-watcher机制)
- [7. Zookeeper的几个典型应用场景](#7-zookeeper的几个典型应用场景)
- [7.1. 选主](#71-选主)
- [7.2. 分布式锁](#72-分布式锁)
- [7.3. 命名服务](#73-命名服务)
- [7.4. 集群管理和注册中心](#74-集群管理和注册中心)
- [8. 总结](#8-总结)
<!-- /code_chunk_output -->
## 1. 好久不见
离上一篇文章的发布也快一个月了,想想已经快一个月没写东西了,其中可能有期末考试、课程设计和驾照考试,但这都不是借口! 离上一篇文章的发布也快一个月了,想想已经快一个月没写东西了,其中可能有期末考试、课程设计和驾照考试,但这都不是借口!
@ -10,7 +43,7 @@
> 文章很长,先赞后看,养成习惯。❤️ 🧡 💛 💚 💙 💜 > 文章很长,先赞后看,养成习惯。❤️ 🧡 💛 💚 💙 💜
## 什么是ZooKeeper ## 2. 什么是ZooKeeper
`ZooKeeper``Yahoo` 开发,后来捐赠给了 `Apache` ,现已成为 `Apache` 顶级项目。`ZooKeeper` 是一个开源的分布式应用程序协调服务器,其为分布式系统提供一致性服务。其一致性是通过基于 `Paxos` 算法的 `ZAB` 协议完成的。其主要功能包括:配置维护、分布式同步、集群管理、分布式事务等。 `ZooKeeper``Yahoo` 开发,后来捐赠给了 `Apache` ,现已成为 `Apache` 顶级项目。`ZooKeeper` 是一个开源的分布式应用程序协调服务器,其为分布式系统提供一致性服务。其一致性是通过基于 `Paxos` 算法的 `ZAB` 协议完成的。其主要功能包括:配置维护、分布式同步、集群管理、分布式事务等。
@ -34,7 +67,7 @@
比如各个分布式组件如何协调起来,如何减少各个系统之间的耦合度,分布式事务的处理,如何去配置整个分布式系统等等。`ZooKeeper` 主要就是解决这些问题的。 比如各个分布式组件如何协调起来,如何减少各个系统之间的耦合度,分布式事务的处理,如何去配置整个分布式系统等等。`ZooKeeper` 主要就是解决这些问题的。
## 一致性问题 ## 3. 一致性问题
设计一个分布式系统必定会遇到一个问题—— **因为分区容忍性partition tolerance的存在就必定要求我们需要在系统可用性availability和数据一致性consistency中做出权衡** 。这就是著名的 `CAP` 定理。 设计一个分布式系统必定会遇到一个问题—— **因为分区容忍性partition tolerance的存在就必定要求我们需要在系统可用性availability和数据一致性consistency中做出权衡** 。这就是著名的 `CAP` 定理。
@ -44,7 +77,7 @@
而上述前者就是 `Eureka` 的处理方式它保证了AP可用性后者就是我们今天所要将的 `ZooKeeper` 的处理方式它保证了CP数据一致性 而上述前者就是 `Eureka` 的处理方式它保证了AP可用性后者就是我们今天所要将的 `ZooKeeper` 的处理方式它保证了CP数据一致性
## 一致性协议和算法 ## 4. 一致性协议和算法
而为了解决数据一致性问题,在科学家和程序员的不断探索中,就出现了很多的一致性协议和算法。比如 2PC两阶段提交3PC三阶段提交Paxos算法等等。 而为了解决数据一致性问题,在科学家和程序员的不断探索中,就出现了很多的一致性协议和算法。比如 2PC两阶段提交3PC三阶段提交Paxos算法等等。
@ -56,7 +89,7 @@
而为什么要去解决数据一致性的问题?你想想,如果一个秒杀系统将服务拆分成了下订单和加积分服务,这两个服务部署在不同的机器上了,万一在消息的传播过程中积分系统宕机了,总不能你这边下了订单却没加积分吧?你总得保证两边的数据需要一致吧? 而为什么要去解决数据一致性的问题?你想想,如果一个秒杀系统将服务拆分成了下订单和加积分服务,这两个服务部署在不同的机器上了,万一在消息的传播过程中积分系统宕机了,总不能你这边下了订单却没加积分吧?你总得保证两边的数据需要一致吧?
### 2PC两阶段提交 ### 4.1. 2PC两阶段提交
两阶段提交是一种保证分布式系统数据一致性的协议,现在很多数据库都是采用的两阶段提交协议来完成 **分布式事务** 的处理。 两阶段提交是一种保证分布式系统数据一致性的协议,现在很多数据库都是采用的两阶段提交协议来完成 **分布式事务** 的处理。
@ -86,7 +119,7 @@
* **阻塞问题**,即当协调者发送 `prepare` 请求,参与者收到之后如果能处理那么它将会进行事务的处理但并不提交,这个时候会一直占用着资源不释放,如果此时协调者挂了,那么这些资源都不会再释放了,这会极大影响性能。 * **阻塞问题**,即当协调者发送 `prepare` 请求,参与者收到之后如果能处理那么它将会进行事务的处理但并不提交,这个时候会一直占用着资源不释放,如果此时协调者挂了,那么这些资源都不会再释放了,这会极大影响性能。
* **数据不一致问题**,比如当第二阶段,协调者只发送了一部分的 `commit` 请求就挂了,那么也就意味着,收到消息的参与者会进行事务的提交,而后面没收到的则不会进行事务提交,那么这时候就会产生数据不一致性问题。 * **数据不一致问题**,比如当第二阶段,协调者只发送了一部分的 `commit` 请求就挂了,那么也就意味着,收到消息的参与者会进行事务的提交,而后面没收到的则不会进行事务提交,那么这时候就会产生数据不一致性问题。
### 3PC三阶段提交 ### 4.2. 3PC三阶段提交
因为2PC存在的一系列问题比如单点容错机制缺陷等等从而产生了 **3PC三阶段提交** 。那么这三阶段又分别是什么呢? 因为2PC存在的一系列问题比如单点容错机制缺陷等等从而产生了 **3PC三阶段提交** 。那么这三阶段又分别是什么呢?
@ -104,13 +137,13 @@
所以,要解决一致性问题还需要靠 `Paxos` 算法⭐️ ⭐️ ⭐️ 。 所以,要解决一致性问题还需要靠 `Paxos` 算法⭐️ ⭐️ ⭐️ 。
### `Paxos` 算法 ### 4.3. `Paxos` 算法
`Paxos` 算法是基于**消息传递且具有高度容错特性的一致性算法**,是目前公认的解决分布式一致性问题最有效的算法之一,**其解决的问题就是在分布式系统中如何就某个值(决议)达成一致** 。 `Paxos` 算法是基于**消息传递且具有高度容错特性的一致性算法**,是目前公认的解决分布式一致性问题最有效的算法之一,**其解决的问题就是在分布式系统中如何就某个值(决议)达成一致** 。
`Paxos` 中主要有三个角色,分别为 `Proposer提案者``Acceptor表决者``Learner学习者``Paxos` 算法和 `2PC` 一样,也有两个阶段,分别为 `Prepare``accept` 阶段。 `Paxos` 中主要有三个角色,分别为 `Proposer提案者``Acceptor表决者``Learner学习者``Paxos` 算法和 `2PC` 一样,也有两个阶段,分别为 `Prepare``accept` 阶段。
#### prepare 阶段 #### 4.3.1. prepare 阶段
* `Proposer提案者`:负责提出 `proposal`,每个提案者在提出提案时都会首先获取到一个 **具有全局唯一性的、递增的提案编号N**,即在整个集群中是唯一的编号 N然后将该编号赋予其要提出的提案在**第一阶段是只将提案编号发送给所有的表决者**。 * `Proposer提案者`:负责提出 `proposal`,每个提案者在提出提案时都会首先获取到一个 **具有全局唯一性的、递增的提案编号N**,即在整个集群中是唯一的编号 N然后将该编号赋予其要提出的提案在**第一阶段是只将提案编号发送给所有的表决者**。
* `Acceptor表决者`:每个表决者在 `accept` 某提案后会将该提案编号N记录在本地这样每个表决者中保存的已经被 accept 的提案中会存在一个**编号最大的提案**,其编号假设为 `maxN`。每个表决者仅会 `accept` 编号大于自己本地 `maxN` 的提案,在批准提案时表决者会将以前接受过的最大编号的提案作为响应反馈给 `Proposer` * `Acceptor表决者`:每个表决者在 `accept` 某提案后会将该提案编号N记录在本地这样每个表决者中保存的已经被 accept 的提案中会存在一个**编号最大的提案**,其编号假设为 `maxN`。每个表决者仅会 `accept` 编号大于自己本地 `maxN` 的提案,在批准提案时表决者会将以前接受过的最大编号的提案作为响应反馈给 `Proposer`
@ -119,7 +152,7 @@
![paxos第一阶段](http://img.francisqiang.top/img/paxos1.jpg) ![paxos第一阶段](http://img.francisqiang.top/img/paxos1.jpg)
#### accept 阶段 #### 4.3.2. accept 阶段
当一个提案被 `Proposer` 提出后,如果 `Proposer` 收到了超过半数的 `Acceptor` 的批准(`Proposer` 本身同意),那么此时 `Proposer` 会给所有的 `Acceptor` 发送真正的提案(你可以理解为第一阶段为试探),这个时候 `Proposer` 就会发送提案的内容和提案编号。 当一个提案被 `Proposer` 提出后,如果 `Proposer` 收到了超过半数的 `Acceptor` 的批准(`Proposer` 本身同意),那么此时 `Proposer` 会给所有的 `Acceptor` 发送真正的提案(你可以理解为第一阶段为试探),这个时候 `Proposer` 就会发送提案的内容和提案编号。
@ -135,7 +168,7 @@
> 对于 `Learner` 来说如何去学习 `Acceptor` 批准的提案内容,这有很多方式,读者可以自己去了解一下,这里不做过多解释。 > 对于 `Learner` 来说如何去学习 `Acceptor` 批准的提案内容,这有很多方式,读者可以自己去了解一下,这里不做过多解释。
#### `paxos` 算法的死循环问题 #### 4.3.3. `paxos` 算法的死循环问题
其实就有点类似于两个人吵架,小明说我是对的,小红说我才是对的,两个人据理力争的谁也不让谁🤬🤬。 其实就有点类似于两个人吵架,小明说我是对的,小红说我才是对的,两个人据理力争的谁也不让谁🤬🤬。
@ -147,15 +180,15 @@
那么如何解决呢?很简单,人多了容易吵架,我现在 **就允许一个能提案** 就行了。 那么如何解决呢?很简单,人多了容易吵架,我现在 **就允许一个能提案** 就行了。
## 引出 `ZAB` ## 5. 引出 `ZAB`
### `Zookeeper` 架构 ### 5.1. `Zookeeper` 架构
作为一个优秀高效且可靠的分布式协调框架,`ZooKeeper` 在解决分布式数据一致性问题时并没有直接使用 `Paxos` ,而是专门定制了一致性协议叫做 `ZAB(ZooKeeper Automic Broadcast)` 原子广播协议,该协议能够很好地支持 **崩溃恢复** 作为一个优秀高效且可靠的分布式协调框架,`ZooKeeper` 在解决分布式数据一致性问题时并没有直接使用 `Paxos` ,而是专门定制了一致性协议叫做 `ZAB(ZooKeeper Automic Broadcast)` 原子广播协议,该协议能够很好地支持 **崩溃恢复**
![Zookeeper架构](http://img.francisqiang.top/img/Zookeeper架构.jpg) ![Zookeeper架构](http://img.francisqiang.top/img/Zookeeper架构.jpg)
### `ZAB` 中的三个角色 ### 5.2. `ZAB` 中的三个角色
和介绍 `Paxos` 一样,在介绍 `ZAB` 协议之前,我们首先来了解一下在 `ZAB` 中三个主要的角色,`Leader 领导者``Follower跟随者``Observer观察者` 和介绍 `Paxos` 一样,在介绍 `ZAB` 协议之前,我们首先来了解一下在 `ZAB` 中三个主要的角色,`Leader 领导者``Follower跟随者``Observer观察者`
@ -165,7 +198,7 @@
`ZAB` 协议中对 `zkServer`(即上面我们说的三个角色的总称) 还有两种模式的定义,分别是 **消息广播****崩溃恢复** `ZAB` 协议中对 `zkServer`(即上面我们说的三个角色的总称) 还有两种模式的定义,分别是 **消息广播****崩溃恢复**
### 消息广播模式 ### 5.3. 消息广播模式
说白了就是 `ZAB` 协议是如何处理写请求的,上面我们不是说只有 `Leader` 能处理写请求嘛?那么我们的 `Follower``Observer` 是不是也需要 **同步更新数据** 呢?总不能数据只在 `Leader` 中更新了,其他角色都没有得到更新吧? 说白了就是 `ZAB` 协议是如何处理写请求的,上面我们不是说只有 `Leader` 能处理写请求嘛?那么我们的 `Follower``Observer` 是不是也需要 **同步更新数据** 呢?总不能数据只在 `Leader` 中更新了,其他角色都没有得到更新吧?
@ -185,7 +218,7 @@
定义这个的原因也是为了顺序性,每个 `proposal``Leader` 中生成后需要 **通过其 `ZXID` 来进行排序** ,才能得到处理。 定义这个的原因也是为了顺序性,每个 `proposal``Leader` 中生成后需要 **通过其 `ZXID` 来进行排序** ,才能得到处理。
### 崩溃恢复模式 ### 5.4. 崩溃恢复模式
说到崩溃恢复我们首先要提到 `ZAB` 中的 `Leader` 选举算法,当系统出现崩溃影响最大应该是 `Leader` 的崩溃,因为我们只有一个 `Leader` ,所以当 `Leader` 出现问题的时候我们势必需要重新选举 `Leader` 说到崩溃恢复我们首先要提到 `ZAB` 中的 `Leader` 选举算法,当系统出现崩溃影响最大应该是 `Leader` 的崩溃,因为我们只有一个 `Leader` ,所以当 `Leader` 出现问题的时候我们势必需要重新选举 `Leader`
@ -229,13 +262,13 @@
![崩溃恢复](http://img.francisqiang.top/img/崩溃恢复2.jpg) ![崩溃恢复](http://img.francisqiang.top/img/崩溃恢复2.jpg)
## Zookeeper的几个理论知识 ## 6. Zookeeper的几个理论知识
了解了 `ZAB` 协议还不够,它仅仅是 `Zookeeper` 内部实现的一种方式,而我们如何通过 `Zookeeper` 去做一些典型的应用场景呢?比如说集群管理,分布式锁,`Master` 选举等等。 了解了 `ZAB` 协议还不够,它仅仅是 `Zookeeper` 内部实现的一种方式,而我们如何通过 `Zookeeper` 去做一些典型的应用场景呢?比如说集群管理,分布式锁,`Master` 选举等等。
这就涉及到如何使用 `Zookeeper` 了,但在使用之前我们还需要掌握几个概念。比如 `Zookeeper`**数据模型** 、**会话机制**、**ACL**、**Watcher机制** 等等。 这就涉及到如何使用 `Zookeeper` 了,但在使用之前我们还需要掌握几个概念。比如 `Zookeeper`**数据模型** 、**会话机制**、**ACL**、**Watcher机制** 等等。
### 数据模型 ### 6.1. 数据模型
`zookeeper` 数据存储结构与标准的 `Unix` 文件系统非常相似,都是在根节点下挂很多子节点(树型)。但是 `zookeeper` 中没有文件系统中目录与文件的概念,而是 **使用了 `znode` 作为数据节点**`znode``zookeeper` 中的最小数据单元,每个 `znode` 上都可以保存数据,同时还可以挂载子节点,形成一个树形化命名空间。 `zookeeper` 数据存储结构与标准的 `Unix` 文件系统非常相似,都是在根节点下挂很多子节点(树型)。但是 `zookeeper` 中没有文件系统中目录与文件的概念,而是 **使用了 `znode` 作为数据节点**`znode``zookeeper` 中的最小数据单元,每个 `znode` 上都可以保存数据,同时还可以挂载子节点,形成一个树形化命名空间。
@ -264,13 +297,13 @@
* `numChildre`该节点的子节点个数如果为临时节点为0。 * `numChildre`该节点的子节点个数如果为临时节点为0。
* `pzxid`该节点子节点列表最后一次被修改时的事务ID注意是子节点的 **列表** ,不是内容。 * `pzxid`该节点子节点列表最后一次被修改时的事务ID注意是子节点的 **列表** ,不是内容。
### 会话 ### 6.2. 会话
我想这个对于后端开发的朋友肯定不陌生,不就是 `session` 吗?只不过 `zk` 客户端和服务端是通过 **`TCP` 长连接** 维持的会话机制,其实对于会话来说你可以理解为 **保持连接状态** 我想这个对于后端开发的朋友肯定不陌生,不就是 `session` 吗?只不过 `zk` 客户端和服务端是通过 **`TCP` 长连接** 维持的会话机制,其实对于会话来说你可以理解为 **保持连接状态**
`zookeeper` 中,会话还有对应的事件,比如 `CONNECTION_LOSS 连接丢失事件``SESSION_MOVED 会话转移事件``SESSION_EXPIRED 会话超时失效事件` `zookeeper` 中,会话还有对应的事件,比如 `CONNECTION_LOSS 连接丢失事件``SESSION_MOVED 会话转移事件``SESSION_EXPIRED 会话超时失效事件`
### ACL ### 6.3. ACL
`ACL``Access Control Lists` ,它是一种权限控制。在 `zookeeper` 中定义了5种权限它们分别为 `ACL``Access Control Lists` ,它是一种权限控制。在 `zookeeper` 中定义了5种权限它们分别为
@ -280,19 +313,19 @@
* `DELETE`:删除子节点的权限。 * `DELETE`:删除子节点的权限。
* `ADMIN`:设置节点 ACL 的权限。 * `ADMIN`:设置节点 ACL 的权限。
### Watcher机制 ### 6.4. Watcher机制
`Watcher` 为事件监听器,是 `zk` 非常重要的一个特性,很多功能都依赖于它,它有点类似于订阅的方式,即客户端向服务端 **注册** 指定的 `watcher` ,当服务端符合了 `watcher` 的某些事件或要求则会 **向客户端发送事件通知** ,客户端收到通知后找到自己定义的 `Watcher` 然后 **执行相应的回调方法** `Watcher` 为事件监听器,是 `zk` 非常重要的一个特性,很多功能都依赖于它,它有点类似于订阅的方式,即客户端向服务端 **注册** 指定的 `watcher` ,当服务端符合了 `watcher` 的某些事件或要求则会 **向客户端发送事件通知** ,客户端收到通知后找到自己定义的 `Watcher` 然后 **执行相应的回调方法**
![watcher机制](http://img.francisqiang.top/img/watcher机制.jpg) ![watcher机制](http://img.francisqiang.top/img/watcher机制.jpg)
## Zookeeper的几个典型应用场景 ## 7. Zookeeper的几个典型应用场景
前面说了这么多的理论知识,你可能听得一头雾水,这些玩意有啥用?能干啥事?别急,听我慢慢道来。 前面说了这么多的理论知识,你可能听得一头雾水,这些玩意有啥用?能干啥事?别急,听我慢慢道来。
![](http://img.francisqiang.top/img/feijie.jpg) ![](http://img.francisqiang.top/img/feijie.jpg)
### 选主 ### 7.1. 选主
还记得上面我们的所说的临时节点吗?因为 `Zookeeper` 的强一致性,能够很好地在保证 **在高并发的情况下保证节点创建的全局唯一性** (即无法重复创建同样的节点)。 还记得上面我们的所说的临时节点吗?因为 `Zookeeper` 的强一致性,能够很好地在保证 **在高并发的情况下保证节点创建的全局唯一性** (即无法重复创建同样的节点)。
@ -306,7 +339,7 @@
总的来说,我们可以完全 **利用 临时节点、节点状态 和 `watcher` 来实现选主的功能**,临时节点主要用来选举,节点状态和`watcher` 可以用来判断 `master` 的活性和进行重新选举。 总的来说,我们可以完全 **利用 临时节点、节点状态 和 `watcher` 来实现选主的功能**,临时节点主要用来选举,节点状态和`watcher` 可以用来判断 `master` 的活性和进行重新选举。
### 分布式锁 ### 7.2. 分布式锁
分布式锁的实现方式有很多种,比如 `Redis` 、数据库 、`zookeeper` 等。个人认为 `zookeeper` 在实现分布式锁这方面是非常非常简单的。 分布式锁的实现方式有很多种,比如 `Redis` 、数据库 、`zookeeper` 等。个人认为 `zookeeper` 在实现分布式锁这方面是非常非常简单的。
@ -330,13 +363,13 @@
具体怎么做呢?其实也很简单,你可以让 **读请求监听比自己小的最后一个写请求节点,写请求只监听比自己小的最后一个节点** ,感兴趣的小伙伴可以自己去研究一下。 具体怎么做呢?其实也很简单,你可以让 **读请求监听比自己小的最后一个写请求节点,写请求只监听比自己小的最后一个节点** ,感兴趣的小伙伴可以自己去研究一下。
### 命名服务 ### 7.3. 命名服务
如何给一个对象设置ID大家可能都会想到 `UUID`,但是 `UUID` 最大的问题就在于它太长了。。。(太长不一定是好事,嘿嘿嘿)。那么在条件允许的情况下,我们能不能使用 `zookeeper` 来实现呢? 如何给一个对象设置ID大家可能都会想到 `UUID`,但是 `UUID` 最大的问题就在于它太长了。。。(太长不一定是好事,嘿嘿嘿)。那么在条件允许的情况下,我们能不能使用 `zookeeper` 来实现呢?
我们之前提到过 `zookeeper` 是通过 **树形结构** 来存储数据节点的,那也就是说,对于每个节点的 **全路径**它必定是唯一的我们可以使用节点的全路径作为命名方式了。而且更重要的是路径是我们可以自己定义的这对于我们对有些有语意的对象的ID设置可以更加便于理解。 我们之前提到过 `zookeeper` 是通过 **树形结构** 来存储数据节点的,那也就是说,对于每个节点的 **全路径**它必定是唯一的我们可以使用节点的全路径作为命名方式了。而且更重要的是路径是我们可以自己定义的这对于我们对有些有语意的对象的ID设置可以更加便于理解。
### 集群管理和注册中心 ### 7.4. 集群管理和注册中心
看到这里是不是觉得 `zookeeper` 实在是太强大了,它怎么能这么能干! 看到这里是不是觉得 `zookeeper` 实在是太强大了,它怎么能这么能干!
@ -352,7 +385,7 @@
![注册中心](http://img.francisqiang.top/img/注册中心.jpg) ![注册中心](http://img.francisqiang.top/img/注册中心.jpg)
## 总结 ## 8. 总结
看到这里的同学实在是太有耐心了👍👍👍,如果觉得我写得不错的话点个赞哈。 看到这里的同学实在是太有耐心了👍👍👍,如果觉得我写得不错的话点个赞哈。

View File

@ -1,185 +0,0 @@
![](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-9-10/56385654.jpg)
## 前言
相信大家对 ZooKeeper 应该不算陌生。但是你真的了解 ZooKeeper 是个什么东西吗?如果别人/面试官让你给他讲讲 ZooKeeper 是个什么东西,你能回答到什么地步呢?
我本人曾经使用过 ZooKeeper 作为 Dubbo 的注册中心,另外在搭建 solr 集群的时候,我使用到了 ZooKeeper 作为 solr 集群的管理工具。前几天,总结项目经验的时候,我突然问自己 ZooKeeper 到底是个什么东西想了半天脑海中只是简单的能浮现出几句话“①Zookeeper 可以被用作注册中心。 ②Zookeeper 是 Hadoop 生态系统的一员;③构建 Zookeeper 集群的时候,使用的服务器最好是奇数台。” 可见,我对于 Zookeeper 的理解仅仅是停留在了表面。
所以,**通过本文,希望带大家稍微详细的了解一下 ZooKeeper 。如果没有学过 ZooKeeper ,那么本文将会是你进入 ZooKeeper 大门的垫脚砖。如果你已经接触过 ZooKeeper ,那么本文将带你回顾一下 ZooKeeper 的一些基础概念。**
最后,**本文只涉及 ZooKeeper 的一些概念,并不涉及 ZooKeeper 的使用以及 ZooKeeper 集群的搭建。** 网上有介绍 ZooKeeper 的使用以及搭建 ZooKeeper 集群的文章,大家有需要可以自行查阅。
## 一 什么是 ZooKeeper
### ZooKeeper 的由来
**下面这段内容摘自《从Paxos到Zookeeper 》第四章第一节的某段内容,推荐大家阅读以下:**
> Zookeeper最早起源于雅虎研究院的一个研究小组。在当时研究人员发现在雅虎内部很多大型系统基本都需要依赖一个类似的系统来进行分布式协调但是这些系统往往都存在分布式单点问题。所以**雅虎的开发人员就试图开发一个通用的无单点问题的分布式协调框架,以便让开发人员将精力集中在处理业务逻辑上。**
>
>关于“ZooKeeper”这个项目的名字其实也有一段趣闻。在立项初期考虑到之前内部很多项目都是使用动物的名字来命名的例如著名的Pig项目),雅虎的工程师希望给这个项目也取一个动物的名字。时任研究院的首席科学家RaghuRamakrishnan开玩笑地说“在这样下去我们这儿就变成动物园了”此话一出大家纷纷表示就叫动物园管理员吧一一一因为各个以动物命名的分布式组件放在一起**雅虎的整个分布式系统看上去就像一个大型的动物园了而Zookeeper正好要用来进行分布式环境的协调一一于是Zookeeper的名字也就由此诞生了。**
### 1.1 ZooKeeper 概览
ZooKeeper 是一个开源的分布式协调服务ZooKeeper框架最初是在“Yahoo!"上构建的,用于以简单而稳健的方式访问他们的应用程序。 后来Apache ZooKeeper成为HadoopHBase和其他分布式框架使用的有组织服务的标准。 例如Apache HBase使用ZooKeeper跟踪分布式数据的状态。**ZooKeeper 的设计目标是将那些复杂且容易出错的分布式一致性服务封装起来,构成一个高效可靠的原语集,并以一系列简单易用的接口提供给用户使用。**
> **原语:** 操作系统或计算机网络用语范畴。是由若干条指令组成的,用于完成一定功能的一个过程。具有不可分割性·即原语的执行必须是连续的,在执行过程中不允许被中断。
**ZooKeeper 是一个典型的分布式数据一致性解决方案,分布式应用程序可以基于 ZooKeeper 实现诸如数据发布/订阅、负载均衡、命名服务、分布式协调/通知、集群管理、Master 选举、分布式锁和分布式队列等功能。**
**Zookeeper 一个最常用的使用场景就是用于担任服务生产者和服务消费者的注册中心(提供发布订阅服务)。** 服务生产者将自己提供的服务注册到Zookeeper中心服务的消费者在进行服务调用的时候先到Zookeeper中查找服务获取到服务生产者的详细信息之后再去调用服务生产者的内容与数据。如下图所示在 Dubbo架构中 Zookeeper 就担任了注册中心这一角色。
![Dubbo](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-9-10/35571782.jpg)
### 1.2 结合个人使用情况的讲一下 ZooKeeper
在我自己做过的项目中,主要使用到了 ZooKeeper 作为 Dubbo 的注册中心(Dubbo 官方推荐使用 ZooKeeper注册中心)。另外在搭建 solr 集群的时候,我使用 ZooKeeper 作为 solr 集群的管理工具。这时ZooKeeper 主要提供下面几个功能1、集群管理容错、负载均衡。2、配置文件的集中管理3、集群的入口。
我个人觉得在使用 ZooKeeper 的时候,最好是使用 集群版的 ZooKeeper 而不是单机版的。官网给出的架构图就描述的是一个集群版的 ZooKeeper 。通常 3 台服务器就可以构成一个 ZooKeeper 集群了。
**为什么最好使用奇数台服务器构成 ZooKeeper 集群?**
所谓的zookeeper容错是指当宕掉几个zookeeper服务器之后剩下的个数必须大于宕掉的个数的话整个zookeeper才依然可用。假如我们的集群中有n台zookeeper服务器那么也就是剩下的服务数必须大于n/2。先说一下结论2n和2n-1的容忍度是一样的都是n-1大家可以先自己仔细想一想这应该是一个很简单的数学问题了。
比如假如我们有3台那么最大允许宕掉1台zookeeper服务器如果我们有4台的的时候也同样只允许宕掉1台。
假如我们有5台那么最大允许宕掉2台zookeeper服务器如果我们有6台的的时候也同样只允许宕掉2台。
综上何必增加那一个不必要的zookeeper呢
## 二 关于 ZooKeeper 的一些重要概念
### 2.1 重要概念总结
- **ZooKeeper 本身就是一个分布式程序只要半数以上节点存活ZooKeeper 就能正常服务)。**
- **为了保证高可用,最好是以集群形态来部署 ZooKeeper这样只要集群中大部分机器是可用的能够容忍一定的机器故障那么 ZooKeeper 本身仍然是可用的。**
- **ZooKeeper 将数据保存在内存中,这也就保证了 高吞吐量和低延迟**但是内存限制了能够存储的容量不太大此限制也是保持znode中存储的数据量较小的进一步原因
- **ZooKeeper 是高性能的。 在“读”多于“写”的应用程序中尤其地高性能,因为“写”会导致所有的服务器间同步状态。**(“读”多于“写”是协调服务的典型场景。)
- **ZooKeeper有临时节点的概念。 当创建临时节点的客户端会话一直保持活动瞬时节点就一直存在。而当会话终结时瞬时节点被删除。持久节点是指一旦这个ZNode被创建了除非主动进行ZNode的移除操作否则这个ZNode将一直保存在Zookeeper上。**
- ZooKeeper 底层其实只提供了两个功能:①管理(存储、读取)用户程序提交的数据;②为用户程序提供数据节点监听服务。
**下面关于会话Session、 Znode、版本、Watcher、ACL概念的总结都在《从Paxos到Zookeeper 》第四章第一节以及第七章第八节有提到,感兴趣的可以看看!**
### 2.2 会话Session
Session 指的是 ZooKeeper 服务器与客户端会话。**在 ZooKeeper 中,一个客户端连接是指客户端和服务器之间的一个 TCP 长连接**。客户端启动的时候,首先会与服务器建立一个 TCP 连接,从第一次连接建立开始,客户端会话的生命周期也开始了。**通过这个连接客户端能够通过心跳检测与服务器保持有效的会话也能够向Zookeeper服务器发送请求并接受响应同时还能够通过该连接接收来自服务器的Watch事件通知。** Session的`sessionTimeout`值用来设置一个客户端会话的超时时间。当由于服务器压力太大、网络故障或是客户端主动断开连接等各种原因导致客户端连接断开时,**只要在`sessionTimeout`规定的时间内能够重新连接上集群中任意一台服务器,那么之前创建的会话仍然有效。**
**在为客户端创建会话之前服务端首先会为每个客户端都分配一个sessionID。由于 sessionID 是 Zookeeper 会话的一个重要标识,许多与会话相关的运行机制都是基于这个 sessionID 的,因此,无论是哪台服务器为客户端分配的 sessionID都务必保证全局唯一。**
### 2.3 Znode
**在谈到分布式的时候,我们通常说的“节点"是指组成集群的每一台机器。然而在Zookeeper中“节点"分为两类第一类同样是指构成集群的机器我们称之为机器节点第二类则是指数据模型中的数据单元我们称之为数据节点一一ZNode。**
Zookeeper将所有数据存储在内存中数据模型是一棵树Znode Tree),由斜杠(/的进行分割的路径就是一个Znode例如/foo/path1。每个上都会保存自己的数据内容同时还会保存一系列属性信息。
**在Zookeeper中node可以分为持久节点和临时节点两类。所谓持久节点是指一旦这个ZNode被创建了除非主动进行ZNode的移除操作否则这个ZNode将一直保存在Zookeeper上。而临时节点就不一样了它的生命周期和客户端会话绑定一旦客户端会话失效那么这个客户端创建的所有临时节点都会被移除。** 另外ZooKeeper还允许用户为每个节点添加一个特殊的属性**SEQUENTIAL**.一旦节点被标记上这个属性那么在这个节点被创建的时候Zookeeper会自动在其节点名后面追加上一个整型数字这个整型数字是一个由父节点维护的自增数字。
### 2.4 版本
在前面我们已经提到Zookeeper 的每个 ZNode 上都会存储数据对应于每个ZNodeZookeeper 都会为其维护一个叫作 **Stat** 的数据结构Stat 中记录了这个 ZNode 的三个数据版本分别是version当前ZNode的版本、cversion当前ZNode子节点的版本和 aversion当前ZNode的ACL版本
### 2.5 Watcher
**Watcher事件监听器是Zookeeper中的一个很重要的特性。Zookeeper允许用户在指定节点上注册一些Watcher并且在一些特定事件触发的时候ZooKeeper服务端会将事件通知到感兴趣的客户端上去该机制是Zookeeper实现分布式协调服务的重要特性。**
### 2.6 ACL
Zookeeper采用ACLAccessControlLists策略来进行权限控制类似于 UNIX 文件系统的权限控制。Zookeeper 定义了如下5种权限。
![](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-9-10/27473480.jpg)
其中尤其需要注意的是CREATE和DELETE这两种权限都是针对子节点的权限控制。
## 三 ZooKeeper 特点
- **顺序一致性:** 从同一客户端发起的事务请求,最终将会严格地按照顺序被应用到 ZooKeeper 中去。
- **原子性:** 所有事务请求的处理结果在整个集群中所有机器上的应用情况是一致的,也就是说,要么整个集群中所有的机器都成功应用了某一个事务,要么都没有应用。
- **单一系统映像 ** 无论客户端连到哪一个 ZooKeeper 服务器上,其看到的服务端数据模型都是一致的。
- **可靠性:** 一旦一次更改请求被应用,更改的结果就会被持久化,直到被下一次更改覆盖。
## 四 ZooKeeper 设计目标
### 4.1 简单的数据模型
ZooKeeper 允许分布式进程通过共享的层次结构命名空间进行相互协调,这与标准文件系统类似。 名称空间由 ZooKeeper 中的数据寄存器组成 - 称为znode这些类似于文件和目录。 与为存储设计的典型文件系统不同ZooKeeper数据保存在内存中这意味着ZooKeeper可以实现高吞吐量和低延迟。
![](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-9-10/94251757.jpg)
### 4.2 可构建集群
**为了保证高可用,最好是以集群形态来部署 ZooKeeper这样只要集群中大部分机器是可用的能够容忍一定的机器故障那么zookeeper本身仍然是可用的。** 客户端在使用 ZooKeeper 时,需要知道集群机器列表,通过与集群中的某一台机器建立 TCP 连接来使用服务客户端使用这个TCP链接来发送请求、获取结果、获取监听事件以及发送心跳包。如果这个连接异常断开了客户端可以连接到另外的机器上。
**ZooKeeper 官方提供的架构图:**
![](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-9-10/68900686.jpg)
上图中每一个Server代表一个安装Zookeeper服务的服务器。组成 ZooKeeper 服务的服务器都会在内存中维护当前的服务器状态,并且每台服务器之间都互相保持着通信。集群间通过 Zab 协议Zookeeper Atomic Broadcast来保持数据的一致性。
### 4.3 顺序访问
**对于来自客户端的每个更新请求ZooKeeper 都会分配一个全局唯一的递增编号,这个编号反应了所有事务操作的先后顺序,应用程序可以使用 ZooKeeper 这个特性来实现更高层次的同步原语。** **这个编号也叫做时间戳——zxidZookeeper Transaction Id**
### 4.4 高性能
**ZooKeeper 是高性能的。 在“读”多于“写”的应用程序中尤其地高性能,因为“写”会导致所有的服务器间同步状态。(“读”多于“写”是协调服务的典型场景。)**
## 五 ZooKeeper 集群角色介绍
**最典型集群模式: Master/Slave 模式(主备模式)**。在这种模式中,通常 Master服务器作为主服务器提供写服务其他的 Slave 服务器从服务器通过异步复制的方式获取 Master 服务器最新的数据提供读服务。
但是,**在 ZooKeeper 中没有选择传统的 Master/Slave 概念而是引入了Leader、Follower 和 Observer 三种角色**。如下图所示
![](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-9-10/89602762.jpg)
**ZooKeeper 集群中的所有机器通过一个 Leader 选举过程来选定一台称为 “Leader” 的机器Leader 既可以为客户端提供写服务又能提供读服务。除了 Leader 外Follower 和 Observer 都只能提供读服务。Follower 和 Observer 唯一的区别在于 Observer 机器不参与 Leader 的选举过程,也不参与写操作的“过半写成功”策略,因此 Observer 机器可以在不影响写性能的情况下提升集群的读性能。**
![](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-9-13/91622395.jpg)
**当 Leader 服务器出现网络中断、崩溃退出与重启等异常情况时ZAB 协议就会进人恢复模式并选举产生新的Leader服务器。这个过程大致是这样的**
1. Leader election选举阶段节点在一开始都处于选举阶段只要有一个节点得到超半数节点的票数它就可以当选准 leader。
2. Discovery发现阶段在这个阶段followers 跟准 leader 进行通信,同步 followers 最近接收的事务提议。
3. Synchronization同步阶段:同步阶段主要是利用 leader 前一阶段获得的最新提议历史,同步集群中所有的副本。同步完成之后
准 leader 才会成为真正的 leader。
4. Broadcast广播阶段
到了这个阶段Zookeeper 集群才能正式对外提供事务服务,并且 leader 可以进行消息广播。同时如果有新的节点加入,还需要对新节点进行同步。
## 六 ZooKeeper &ZAB 协议&Paxos算法
### 6.1 ZAB 协议&Paxos算法
Paxos 算法应该可以说是 ZooKeeper 的灵魂了。但是ZooKeeper 并没有完全采用 Paxos算法 ,而是使用 ZAB 协议作为其保证数据一致性的核心算法。另外在ZooKeeper的官方文档中也指出ZAB协议并不像 Paxos 算法那样是一种通用的分布式一致性算法它是一种特别为Zookeeper设计的崩溃可恢复的原子消息广播算法。
### 6.2 ZAB 协议介绍
**ZABZooKeeper Atomic Broadcast 原子广播) 协议是为分布式协调服务 ZooKeeper 专门设计的一种支持崩溃恢复的原子广播协议。 在 ZooKeeper 中,主要依赖 ZAB 协议来实现分布式数据一致性基于该协议ZooKeeper 实现了一种主备模式的系统架构来保持集群中各个副本之间的数据一致性。**
### 6.3 ZAB 协议两种基本的模式:崩溃恢复和消息广播
ZAB协议包括两种基本的模式分别是 **崩溃恢复和消息广播**。当整个服务框架在启动过程中,或是当 Leader 服务器出现网络中断、崩溃退出与重启等异常情况时ZAB 协议就会进人恢复模式并选举产生新的Leader服务器。当选举产生了新的 Leader 服务器同时集群中已经有过半的机器与该Leader服务器完成了状态同步之后ZAB协议就会退出恢复模式。其中**所谓的状态同步是指数据同步用来保证集群中存在过半的机器能够和Leader服务器的数据状态保持一致**。
**当集群中已经有过半的Follower服务器完成了和Leader服务器的状态同步那么整个服务框架就可以进人消息广播模式了。** 当一台同样遵守ZAB协议的服务器启动后加人到集群中时如果此时集群中已经存在一个Leader服务器在负责进行消息广播那么新加人的服务器就会自觉地进人数据恢复模式找到Leader所在的服务器并与其进行数据同步然后一起参与到消息广播流程中去。正如上文介绍中所说的ZooKeeper设计成只允许唯一的一个Leader服务器来进行事务请求的处理。Leader服务器在接收到客户端的事务请求后会生成对应的事务提案并发起一轮广播协议而如果集群中的其他机器接收到客户端的事务请求那么这些非Leader服务器会首先将这个事务请求转发给Leader服务器。
关于 **ZAB 协议&Paxos算法** 需要讲和理解的东西太多了,说实话,笔主到现在不太清楚这俩兄弟的具体原理和实现过程。推荐阅读下面两篇文章:
- [图解 Paxos 一致性协议](http://codemacro.com/2014/10/15/explain-poxos/)
- [Zookeeper ZAB 协议分析](https://dbaplus.cn/news-141-1875-1.html)
关于如何使用 zookeeper 实现分布式锁,可以查看下面这篇文章:
-
[10分钟看懂基于Zookeeper的分布式锁](https://blog.csdn.net/qiangcuo6087/article/details/79067136)
## 六 总结
通过阅读本文,想必大家已从 **①ZooKeeper的由来。** -> **②ZooKeeper 到底是什么 。**-> **③ ZooKeeper 的一些重要概念**会话Session、 Znode、版本、Watcher、ACL-> **④ZooKeeper 的特点。** -> **⑤ZooKeeper 的设计目标。**-> **⑥ ZooKeeper 集群角色介绍** Leader、Follower 和 Observer 三种角色)-> **⑦ZooKeeper &ZAB 协议&Paxos算法。** 这七点了解了 ZooKeeper 。
## 参考
- 《从Paxos到Zookeeper 》
- https://cwiki.apache.org/confluence/display/ZOOKEEPER/ProjectDescription
- https://cwiki.apache.org/confluence/display/ZOOKEEPER/Index
- https://www.cnblogs.com/raphael5200/p/5285583.html
- https://zhuanlan.zhihu.com/p/30024403

View File

@ -1,200 +0,0 @@
<!-- MarkdownTOC -->
- [ZooKeeper 数据模型](#zookeeper-数据模型)
- [ZNode\(数据节点\)的结构](#znode数据节点的结构)
- [测试 ZooKeeper 中的常见操作](#测试-zookeeper-中的常见操作)
- [连接 ZooKeeper 服务](#连接-zookeeper-服务)
- [查看常用命令\(help 命令\)](#查看常用命令help-命令)
- [创建节点\(create 命令\)](#创建节点create-命令)
- [更新节点数据内容\(set 命令\)](#更新节点数据内容set-命令)
- [获取节点的数据\(get 命令\)](#获取节点的数据get-命令)
- [查看某个目录下的子节点\(ls 命令\)](#查看某个目录下的子节点ls-命令)
- [查看节点状态\(stat 命令\)](#查看节点状态stat-命令)
- [查看节点信息和状态\(ls2 命令\)](#查看节点信息和状态ls2-命令)
- [删除节点\(delete 命令\)](#删除节点delete-命令)
- [参考](#参考)
<!-- /MarkdownTOC -->
> 看本文之前如果你没有安装 ZooKeeper 的话,可以参考这篇文章:[《使用 SpringBoot+Dubbo 搭建一个简单分布式服务》](https://github.com/Snailclimb/springboot-integration-examples/blob/master/md/springboot-dubbo.md) 的 “开始实战 1 zookeeper 环境安装搭建” 这部分进行安装Centos7.4 环境下)。如果你想对 ZooKeeper 有一个整体了解的话,可以参考这篇文章:[《可能是把 ZooKeeper 概念讲的最清楚的一篇文章》](https://github.com/Snailclimb/JavaGuide/blob/master/%E4%B8%BB%E6%B5%81%E6%A1%86%E6%9E%B6/ZooKeeper.md)
### ZooKeeper 数据模型
ZNode数据节点是 ZooKeeper 中数据的最小单元每个ZNode上都可以保存数据同时还是可以有子节点这就像树结构一样如下图所示。可以看出节点路径标识方式和Unix文件
系统路径非常相似,都是由一系列使用斜杠"/"进行分割的路径表示,开发人员可以向这个节点中写人数据,也可以在节点下面创建子节点。这些操作我们后面都会介绍到。
![ZooKeeper 数据模型](https://images.gitbook.cn/95a192b0-1c56-11e9-9a8e-f3b01b1ea9aa)
提到 ZooKeeper 数据模型,还有一个不得不得提的东西就是 **事务 ID** 。事务的ACIDAtomic原子性Consistency:一致性Isolation隔离性Durability持久性四大特性我在这里就不多说了相信大家也已经挺腻了。
在Zookeeper中事务是指能够改变 ZooKeeper 服务器状态的操作,我们也称之为事务操作或更新操作,一般包括数据节点创建与删除、数据节点内容更新和客户端会话创建与失效等操作。**对于每一个事务请求ZooKeeper 都会为其分配一个全局唯一的事务ID,用 ZXID 来表示**通常是一个64位的数字。每一个ZXID对应一次更新操作**从这些 ZXID 中可以间接地识别出Zookeeper处理这些更新操作请求的全局顺序**。
### ZNode(数据节点)的结构
每个 ZNode 由2部分组成:
- stat状态信息
- data数据内容
如下所示,我通过 get 命令来获取 根目录下的 dubbo 节点的内容。get 命令在下面会介绍到)
```shell
[zk: 127.0.0.1:2181(CONNECTED) 6] get /dubbo
# 该数据节点关联的数据内容为空
null
# 下面是该数据节点的一些状态信息,其实就是 Stat 对象的格式化输出
cZxid = 0x2
ctime = Tue Nov 27 11:05:34 CST 2018
mZxid = 0x2
mtime = Tue Nov 27 11:05:34 CST 2018
pZxid = 0x3
cversion = 1
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 0
numChildren = 1
```
这些状态信息其实就是 Stat 对象的格式化输出。Stat 类中包含了一个数据节点的所有状态信息的字段包括事务ID、版本信息和子节点个数等如下图所示图源《从Paxos到Zookeeper 分布式一致性原理与实践》,下面会介绍通过 stat 命令查看数据节点的状态)。
**Stat 类:**
![Stat 类](https://images.gitbook.cn/a841e740-1c55-11e9-b5b7-abf0ec0c666a)
关于数据节点的状态信息说明也就是对Stat 类中的各字段进行说明可以参考下图图源《从Paxos到Zookeeper 分布式一致性原理与实践》)。
![数据节点的状态信息说明](https://images.gitbook.cn/f44d8630-1c55-11e9-b5b7-abf0ec0c666a)
### 测试 ZooKeeper 中的常见操作
#### 连接 ZooKeeper 服务
进入安装 ZooKeeper文件夹的 bin 目录下执行下面的命令连接 ZooKeeper 服务Linux环境下连接之前首选要确定你的 ZooKeeper 服务已经启动成功)。
```shell
./zkCli.sh -server 127.0.0.1:2181
```
![连接 ZooKeeper 服务](https://images.gitbook.cn/153b84c0-1c59-11e9-9a8e-f3b01b1ea9aa)
从上图可以看出控制台打印出了很多信息包括我们的主机名称、JDK 版本、操作系统等等。如果你成功看到这些信息,说明你成功连接到 ZooKeeper 服务。
#### 查看常用命令(help 命令)
help 命令查看 zookeeper 常用命令
![help 命令](https://images.gitbook.cn/091db640-1c59-11e9-b5b7-abf0ec0c666a)
#### 创建节点(create 命令)
通过 create 命令在根目录创建了node1节点与它关联的字符串是"node1"
```shell
[zk: 127.0.0.1:2181(CONNECTED) 34] create /node1 “node1”
```
通过 create 命令在根目录创建了node1节点与它关联的内容是数字 123
```shell
[zk: 127.0.0.1:2181(CONNECTED) 1] create /node1/node1.1 123
Created /node1/node1.1
```
#### 更新节点数据内容(set 命令)
```shell
[zk: 127.0.0.1:2181(CONNECTED) 11] set /node1 "set node1"
```
#### 获取节点的数据(get 命令)
get 命令可以获取指定节点的数据内容和节点的状态,可以看出我们通过set 命令已经将节点数据内容改为 "set node1"。
```shell
set node1
cZxid = 0x47
ctime = Sun Jan 20 10:22:59 CST 2019
mZxid = 0x4b
mtime = Sun Jan 20 10:41:10 CST 2019
pZxid = 0x4a
cversion = 1
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 9
numChildren = 1
```
#### 查看某个目录下的子节点(ls 命令)
通过 ls 命令查看根目录下的节点
```shell
[zk: 127.0.0.1:2181(CONNECTED) 37] ls /
[dubbo, zookeeper, node1]
```
通过 ls 命令查看 node1 目录下的节点
```shell
[zk: 127.0.0.1:2181(CONNECTED) 5] ls /node1
[node1.1]
```
zookeeper 中的 ls 命令和 linux 命令中的 ls 类似, 这个命令将列出绝对路径path下的所有子节点信息列出1级并不递归
#### 查看节点状态(stat 命令)
通过 stat 命令查看节点状态
```shell
[zk: 127.0.0.1:2181(CONNECTED) 10] stat /node1
cZxid = 0x47
ctime = Sun Jan 20 10:22:59 CST 2019
mZxid = 0x47
mtime = Sun Jan 20 10:22:59 CST 2019
pZxid = 0x4a
cversion = 1
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 11
numChildren = 1
```
上面显示的一些信息比如cversion、aclVersion、numChildren等等我在上面 “ZNode(数据节点)的结构” 这部分已经介绍到。
#### 查看节点信息和状态(ls2 命令)
ls2 命令更像是 ls 命令和 stat 命令的结合。ls2 命令返回的信息包括2部分子节点列表 + 当前节点的stat信息。
```shell
[zk: 127.0.0.1:2181(CONNECTED) 7] ls2 /node1
[node1.1]
cZxid = 0x47
ctime = Sun Jan 20 10:22:59 CST 2019
mZxid = 0x47
mtime = Sun Jan 20 10:22:59 CST 2019
pZxid = 0x4a
cversion = 1
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 11
numChildren = 1
```
#### 删除节点(delete 命令)
这个命令很简单,但是需要注意的一点是如果你要删除某一个节点,那么这个节点必须无子节点才行。
```shell
[zk: 127.0.0.1:2181(CONNECTED) 3] delete /node1/node1.1
```
在后面我会介绍到 Java 客户端 API的使用以及开源 Zookeeper 客户端 ZkClient 和 Curator 的使用。
### 参考
- 《从Paxos到Zookeeper 分布式一致性原理与实践》

View File

@ -0,0 +1,323 @@
<!-- @import "[TOC]" {cmd="toc" depthFrom=1 depthTo=6 orderedList=false} -->
<!-- code_chunk_output -->
- [1. 前言](#1-前言)
- [2. ZooKeeper 安装和使用](#2-zookeeper-安装和使用)
- [2.1. 使用Docker 安装 zookeeper](#21-使用docker-安装-zookeeper)
- [2.2. 连接 ZooKeeper 服务](#22-连接-zookeeper-服务)
- [2.3. 常用命令演示](#23-常用命令演示)
- [2.3.1. 查看常用命令(help 命令)](#231-查看常用命令help-命令)
- [2.3.2. 创建节点(create 命令)](#232-创建节点create-命令)
- [2.3.3. 更新节点数据内容(set 命令)](#233-更新节点数据内容set-命令)
- [2.3.4. 获取节点的数据(get 命令)](#234-获取节点的数据get-命令)
- [2.3.5. 查看某个目录下的子节点(ls 命令)](#235-查看某个目录下的子节点ls-命令)
- [2.3.6. 查看节点状态(stat 命令)](#236-查看节点状态stat-命令)
- [2.3.7. 查看节点信息和状态(ls2 命令)](#237-查看节点信息和状态ls2-命令)
- [2.3.8. 删除节点(delete 命令)](#238-删除节点delete-命令)
- [3. ZooKeeper Java客户端 Curator简单使用](#3-zookeeper-java客户端-curator简单使用)
- [3.1. 连接 ZooKeeper 客户端](#31-连接-zookeeper-客户端)
- [3.2. 数据节点的增删改查](#32-数据节点的增删改查)
- [3.2.1. 创建节点](#321-创建节点)
- [3.2.2. 删除节点](#322-删除节点)
- [3.2.3. 获取/更新节点数据内容](#323-获取更新节点数据内容)
- [3.2.4. 获取某个节点的所有子节点路径](#324-获取某个节点的所有子节点路径)
<!-- /code_chunk_output -->
## 1. 前言
这篇文章简单给演示一下 ZooKeeper 常见命令的使用以及 ZooKeeper Java客户端 Curator 的基本使用。介绍到的内容都是最基本的操作,能满足日常工作的基本需要。
如果文章有任何需要改善和完善的地方,欢迎在评论区指出,共同进步!
## 2. ZooKeeper 安装和使用
### 2.1. 使用Docker 安装 zookeeper
**a.使用 Docker 下载 ZooKeeper**
```shell
docker pull zookeeper:3.5.8
```
**b.运行 ZooKeeper**
```shell
docker run -d --name zookeeper -p 2181:2181 zookeeper:3.5.8
```
### 2.2. 连接 ZooKeeper 服务
**a.进入ZooKeeper容器中**
先使用 `docker ps` 查看 ZooKeeper 的 ContainerID然后使用 `docker exec -it ContainerID /bin/bash` 命令进入容器中。
**b.先进入 bin 目录,然后通过 `./zkCli.sh -server 127.0.0.1:2181`命令连接ZooKeeper 服务**
```bash
root@eaf70fc620cb:/apache-zookeeper-3.5.8-bin# cd bin
```
如果你看到控制台成功打印出如下信息的话,说明你已经成功连接 ZooKeeper 服务。
![](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/2020-8/image-20200805144047420.png)
### 2.3. 常用命令演示
#### 2.3.1. 查看常用命令(help 命令)
通过 `help` 命令查看 ZooKeeper 常用命令
#### 2.3.2. 创建节点(create 命令)
通过 `create` 命令在根目录创建了 node1 节点,与它关联的字符串是"node1"
```shell
[zk: 127.0.0.1:2181(CONNECTED) 34] create /node1 “node1”
```
通过 `create` 命令在根目录创建了 node1 节点,与它关联的内容是数字 123
```shell
[zk: 127.0.0.1:2181(CONNECTED) 1] create /node1/node1.1 123
Created /node1/node1.1
```
#### 2.3.3. 更新节点数据内容(set 命令)
```shell
[zk: 127.0.0.1:2181(CONNECTED) 11] set /node1 "set node1"
```
#### 2.3.4. 获取节点的数据(get 命令)
`get` 命令可以获取指定节点的数据内容和节点的状态,可以看出我们通过 `set` 命令已经将节点数据内容改为 "set node1"。
```shell
set node1
cZxid = 0x47
ctime = Sun Jan 20 10:22:59 CST 2019
mZxid = 0x4b
mtime = Sun Jan 20 10:41:10 CST 2019
pZxid = 0x4a
cversion = 1
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 9
numChildren = 1
```
#### 2.3.5. 查看某个目录下的子节点(ls 命令)
通过 `ls` 命令查看根目录下的节点
```shell
[zk: 127.0.0.1:2181(CONNECTED) 37] ls /
[dubbo, ZooKeeper, node1]
```
通过 `ls` 命令查看 node1 目录下的节点
```shell
[zk: 127.0.0.1:2181(CONNECTED) 5] ls /node1
[node1.1]
```
ZooKeeper 中的 ls 命令和 linux 命令中的 ls 类似, 这个命令将列出绝对路径 path 下的所有子节点信息(列出 1 级,并不递归)
#### 2.3.6. 查看节点状态(stat 命令)
通过 `stat` 命令查看节点状态
```shell
[zk: 127.0.0.1:2181(CONNECTED) 10] stat /node1
cZxid = 0x47
ctime = Sun Jan 20 10:22:59 CST 2019
mZxid = 0x47
mtime = Sun Jan 20 10:22:59 CST 2019
pZxid = 0x4a
cversion = 1
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 11
numChildren = 1
```
上面显示的一些信息比如 cversion、aclVersion、numChildren 等等,我在上面 “znode(数据节点)的结构” 这部分已经介绍到。
#### 2.3.7. 查看节点信息和状态(ls2 命令)
`ls2` 命令更像是 `ls` 命令和 `stat` 命令的结合。 `ls2` 命令返回的信息包括 2 部分:
1. 子节点列表
2. 当前节点的 stat 信息。
```shell
[zk: 127.0.0.1:2181(CONNECTED) 7] ls2 /node1
[node1.1]
cZxid = 0x47
ctime = Sun Jan 20 10:22:59 CST 2019
mZxid = 0x47
mtime = Sun Jan 20 10:22:59 CST 2019
pZxid = 0x4a
cversion = 1
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 11
numChildren = 1
```
#### 2.3.8. 删除节点(delete 命令)
这个命令很简单,但是需要注意的一点是如果你要删除某一个节点,那么这个节点必须无子节点才行。
```shell
[zk: 127.0.0.1:2181(CONNECTED) 3] delete /node1/node1.1
```
在后面我会介绍到 Java 客户端 API 的使用以及开源 ZooKeeper 客户端 ZkClient 和 Curator 的使用。
## 3. ZooKeeper Java客户端 Curator简单使用
Curator 是Netflix公司开源的一套 ZooKeeper Java客户端框架相比于 Zookeeper 自带的客户端 zookeeper 来说Curator 的封装更加完善,各种 API 都可以比较方便地使用。
![](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/2020-8/ph-quote.png)
下面我们就来简单地演示一下 Curator 的使用吧!
Curator4.0+版本对ZooKeeper 3.5.x支持比较好。开始之前请先将下面的依赖添加进你的项目。
```xml
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>4.2.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>4.2.0</version>
</dependency>
```
### 3.1. 连接 ZooKeeper 客户端
通过 `CuratorFrameworkFactory` 创建 `CuratorFramework` 对象,然后再调用 `CuratorFramework` 对象的 `start()` 方法即可!
```java
private static final int BASE_SLEEP_TIME = 1000;
private static final int MAX_RETRIES = 3;
// Retry strategy. Retry 3 times, and will increase the sleep time between retries.
RetryPolicy retryPolicy = new ExponentialBackoffRetry(BASE_SLEEP_TIME, MAX_RETRIES);
CuratorFramework zkClient = CuratorFrameworkFactory.builder()
// the server to connect to (can be a server list)
.connectString("127.0.0.1:2181")
.retryPolicy(retryPolicy)
.build();
zkClient.start();
```
对于一些基本参数的说明:
- `baseSleepTimeMs`:重试之间等待的初始时间
- `maxRetries` :最大重试次数
- `connectString` :要连接的服务器列表
- `retryPolicy` :重试策略
### 3.2. 数据节点的增删改查
#### 3.2.1. 创建节点
我们在 [ZooKeeper常见概念解读](./zookeeper-intro.md) 中介绍到,我们通常是将 znode 分为 4 大类:
- **持久PERSISTENT节点** :一旦创建就一直存在即使 ZooKeeper 集群宕机,直到将其删除。
- **临时EPHEMERAL节点** :临时节点的生命周期是与 **客户端会话session** 绑定的,**会话消失则节点消失** 。并且,临时节点 **只能做叶子节点** ,不能创建子节点。
- **持久顺序PERSISTENT_SEQUENTIAL节点** 除了具有持久PERSISTENT节点的特性之外 子节点的名称还具有顺序性。比如 `/node1/app0000000001``/node1/app0000000002`
- **临时顺序EPHEMERAL_SEQUENTIAL节点** 除了具备临时EPHEMERAL节点的特性之外子节点的名称还具有顺序性。
你在使用的ZooKeeper 的时候,会发现 `CreateMode` 类中实际有 7种 znode 类型 ,但是用的最多的还是上面介绍的 4 种。
**a.创建持久化节点**
你可以通过下面两种方式创建持久化的节点。
```java
//注意:下面的代码会报错,下文说了具体原因
zkClient.create().forPath("/node1/00001");
zkClient.create().withMode(CreateMode.PERSISTENT).forPath("/node1/00002");
```
但是,你运行上面的代码会报错,这是因为的父节点`node1`还未创建。
你可以先创建父节点 `node1` ,然后再执行上面的代码就不会报错了。
```java
zkClient.create().forPath("/node1");
```
更推荐的方式是通过下面这行代码, **`creatingParentsIfNeeded()` 可以保证父节点不存在的时候自动创建父节点,这是非常有用的。**
```java
zkClient.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).forPath("/node1/00001");
```
**b.创建临时节点**
```java
zkClient.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).forPath("/node1/00001");
```
**c.创建节点并指定数据内容**
```java
zkClient.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).forPath("/node1/00001","java".getBytes());
zkClient.getData().forPath("/node1/00001");//获取节点的数据内容,获取到的是 byte数组
```
**d.检测节点是否创建成功**
```java
zkClient.checkExists().forPath("/node1/00001");//不为null的话说明节点创建成功
```
#### 3.2.2. 删除节点
**a.删除一个子节点**
```java
zkClient.delete().forPath("/node1/00001");
```
**b.删除一个节点以及其下的所有子节点**
```java
zkClient.delete().deletingChildrenIfNeeded().forPath("/node1");
```
#### 3.2.3. 获取/更新节点数据内容
```java
zkClient.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).forPath("/node1/00001","java".getBytes());
zkClient.getData().forPath("/node1/00001");//获取节点的数据内容
zkClient.setData().forPath("/node1/00001","c++".getBytes());//更新节点数据内容
```
#### 3.2.4. 获取某个节点的所有子节点路径
```java
List<String> childrenPaths = zkClient.getChildren().forPath("/node1");
```

View File

@ -0,0 +1,297 @@
<!-- @import "[TOC]" {cmd="toc" depthFrom=1 depthTo=6 orderedList=false} -->
<!-- code_chunk_output -->
- [1. 前言](#1-前言)
- [2. ZooKeeper 介绍](#2-zookeeper-介绍)
- [2.1. ZooKeeper 由来](#21-zookeeper-由来)
- [2.2. ZooKeeper 概览](#22-zookeeper-概览)
- [2.3. ZooKeeper 特点](#23-zookeeper-特点)
- [2.4. ZooKeeper 典型应用场景](#24-zookeeper-典型应用场景)
- [2.5. 有哪些著名的开源项目用到了 ZooKeeper?](#25-有哪些著名的开源项目用到了-zookeeper)
- [3. ZooKeeper 重要概念解读](#3-zookeeper-重要概念解读)
- [3.1. Data model数据模型](#31-data-model数据模型)
- [3.2. znode数据节点](#32-znode数据节点)
- [3.2.1. znode 4种类型](#321-znode-4种类型)
- [3.2.2. znode 数据结构](#322-znode-数据结构)
- [3.3. 版本version](#33-版本version)
- [3.4. ACL权限控制](#34-acl权限控制)
- [3.5. Watcher事件监听器](#35-watcher事件监听器)
- [3.6. 会话Session](#36-会话session)
- [4. ZooKeeper 集群](#4-zookeeper-集群)
- [4.1. ZooKeeper 集群角色](#41-zookeeper-集群角色)
- [4.2. ZooKeeper 集群中的服务器状态](#42-zookeeper-集群中的服务器状态)
- [4.3. ZooKeeper 集群为啥最好奇数台?](#43-zookeeper-集群为啥最好奇数台)
- [5. ZAB 协议和Paxos 算法](#5-zab-协议和paxos-算法)
- [5.1. ZAB 协议介绍](#51-zab-协议介绍)
- [5.2. ZAB 协议两种基本的模式:崩溃恢复和消息广播](#52-zab-协议两种基本的模式崩溃恢复和消息广播)
- [6. 总结](#6-总结)
- [7. 参考](#7-参考)
<!-- /code_chunk_output -->
## 1. 前言
相信大家对 ZooKeeper 应该不算陌生。但是你真的了解 ZooKeeper 到底有啥用不?如果别人/面试官让你给他讲讲对于 ZooKeeper 的认识,你能回答到什么地步呢?
拿我自己来说吧!我本人曾经使用 Dubbo 来做分布式项目的时候,使用了 ZooKeeper 作为注册中心。为了保证分布式系统能够同步访问某个资源,我还使用 ZooKeeper 做过分布式锁。另外,我在学习 Kafka 的时候,知道 Kafka 很多功能的实现依赖了 ZooKeeper。
前几天,总结项目经验的时候,我突然问自己 ZooKeeper 到底是个什么东西?想了半天,脑海中只是简单的能浮现出几句话:
1. ZooKeeper 可以被用作注册中心、分布式锁;
2. ZooKeeper 是 Hadoop 生态系统的一员;
3. 构建 ZooKeeper 集群的时候,使用的服务器最好是奇数台。
由此可见,我对于 ZooKeeper 的理解仅仅是停留在了表面。
所以,通过本文,希望带大家稍微详细的了解一下 ZooKeeper 。如果没有学过 ZooKeeper ,那么本文将会是你进入 ZooKeeper 大门的垫脚砖。如果你已经接触过 ZooKeeper ,那么本文将带你回顾一下 ZooKeeper 的一些基础概念。
另外,本文不光会涉及到 ZooKeeper 的一些概念,后面的文章会介绍到 ZooKeeper 常见命令的使用以及使用 Apache Curator 作为 ZooKeeper 的客户端。
## 2. ZooKeeper 介绍
### 2.1. ZooKeeper 由来
正式介绍 ZooKeeper 之前,我们先来看看 ZooKeeper 的由来,还挺有意思的。
下面这段内容摘自《从 Paxos 到 ZooKeeper 》第四章第一节,推荐大家阅读以下:
> ZooKeeper 最早起源于雅虎研究院的一个研究小组。在当时,研究人员发现,在雅虎内部很多大型系统基本都需要依赖一个类似的系统来进行分布式协调,但是这些系统往往都存在分布式单点问题。所以,雅虎的开发人员就试图开发一个通用的无单点问题的分布式协调框架,以便让开发人员将精力集中在处理业务逻辑上。
>
> 关于“ZooKeeper”这个项目的名字其实也有一段趣闻。在立项初期考虑到之前内部很多项目都是使用动物的名字来命名的例如著名的 Pig 项目),雅虎的工程师希望给这个项目也取一个动物的名字。时任研究院的首席科学家 RaghuRamakrishnan 开玩笑地说:“在这样下去,我们这儿就变成动物园了!”此话一出,大家纷纷表示就叫动物园管理员吧一一一因为各个以动物命名的分布式组件放在一起,雅虎的整个分布式系统看上去就像一个大型的动物园了,而 ZooKeeper 正好要用来进行分布式环境的协调一一于是ZooKeeper 的名字也就由此诞生了。
### 2.2. ZooKeeper 概览
ZooKeeper 是一个开源的**分布式协调服务**,它的设计目标是将那些复杂且容易出错的分布式一致性服务封装起来,构成一个高效可靠的原语集,并以一系列简单易用的接口提供给用户使用。
> **原语:** 操作系统或计算机网络用语范畴。是由若干条指令组成的,用于完成一定功能的一个过程。具有不可分割性·即原语的执行必须是连续的,在执行过程中不允许被中断。
**ZooKeeper 为我们提供了高可用、高性能、稳定的分布式数据一致性解决方案,通常被用于实现诸如数据发布/订阅、负载均衡、命名服务、分布式协调/通知、集群管理、Master 选举、分布式锁和分布式队列等功能。**
另外,**ZooKeeper 将数据保存在内存中,性能是非常棒的。 在“读”多于“写”的应用程序中尤其地高性能,因为“写”会导致所有的服务器间同步状态。(“读”多于“写”是协调服务的典型场景)。**
### 2.3. ZooKeeper 特点
- **顺序一致性:** 从同一客户端发起的事务请求,最终将会严格地按照顺序被应用到 ZooKeeper 中去。
- **原子性:** 所有事务请求的处理结果在整个集群中所有机器上的应用情况是一致的,也就是说,要么整个集群中所有的机器都成功应用了某一个事务,要么都没有应用。
- **单一系统映像 ** 无论客户端连到哪一个 ZooKeeper 服务器上,其看到的服务端数据模型都是一致的。
- **可靠性:** 一旦一次更改请求被应用,更改的结果就会被持久化,直到被下一次更改覆盖。
### 2.4. ZooKeeper 典型应用场景
ZooKeeper 概览中,我们介绍到使用其通常被用于实现诸如数据发布/订阅、负载均衡、命名服务、分布式协调/通知、集群管理、Master 选举、分布式锁和分布式队列等功能。
下面选 3 个典型的应用场景来专门说说:
1. **分布式锁** 通过创建唯一节点获得分布式锁,当获得锁的一方执行完相关代码或者是挂掉之后就释放锁。
2. **命名服务** :可以通过 ZooKeeper 的顺序节点生成全局唯一 ID
3. **数据发布/订阅** :通过 **Watcher 机制** 可以很方便地实现数据发布/订阅。当你将数据发布到 ZooKeeper 被监听的节点上,其他机器可通过监听 ZooKeeper 上节点的变化来实现配置的动态更新。
实际上,这些功能的实现基本都得益于 ZooKeeper 可以保存数据的功能,但是 ZooKeeper 不适合保存大量数据,这一点需要注意。
### 2.5. 有哪些著名的开源项目用到了 ZooKeeper?
1. **Kafka** : ZooKeeper 主要为 Kafka 提供 Broker 和 Topic 的注册以及多个 Partition 的负载均衡等功能。
2. **Hbase** : ZooKeeper 为 Hbase 提供确保整个集群只有一个 Master 以及保存和提供 regionserver 状态信息(是否在线)等功能。
3. **Hadoop** : ZooKeeper 为 Namenode 提供高可用支持。
## 3. ZooKeeper 重要概念解读
_破音拿出小本本下面的内容非常重要哦_
### 3.1. Data model数据模型
ZooKeeper 数据模型采用层次化的多叉树形结构,每个节点上都可以存储数据,这些数据可以是数字、字符串或者是二级制序列。并且。每个节点还可以拥有 N 个子结点,最上层是根节点以“/”来代表。每个数据节点在 ZooKeeper 中被称为 **znode **,它是 ZooKeeper 中数据的最小单元。并且,每个 znode 都一个唯一的路径标识。
强调一句:**ZooKeeper 主要是用来协调服务的,而不是用来存储业务数据的,所以不要放比较大的数据在 znode 上ZooKeeper 给出的上线是每个结点的数据大小最大是 1M。**
从下图可以更直观地看出ZooKeeper 节点路径标识方式和 Unix 文件系统路径非常相似,都是由一系列使用斜杠"/"进行分割的路径表示,开发人员可以向这个节点中写人数据,也可以在节点下面创建子节点。这些操作我们后面都会介绍到。
![ZooKeeper 数据模型](https://images.gitbook.cn/95a192b0-1c56-11e9-9a8e-f3b01b1ea9aa)
### 3.2. znode数据节点
介绍了 ZooKeeper 树形数据模型之后,我们知道每个数据节点在 ZooKeeper 中被称为 **znode **,它是 ZooKeeper 中数据的最小单元。你要存放的数据就放在上面,是你使用 ZooKeeper 过程中经常需要解除到的一个概念。
#### 3.2.1. znode 4种类型
我们通常是将 znode 分为 4 大类:
- **持久PERSISTENT节点** :一旦创建就一直存在即使 ZooKeeper 集群宕机,直到将其删除。
- **临时EPHEMERAL节点** :临时节点的生命周期是与 **客户端会话session** 绑定的,**会话消失则节点消失** 。并且,临时节点 **只能做叶子节点** ,不能创建子节点。
- **持久顺序PERSISTENT_SEQUENTIAL节点** 除了具有持久PERSISTENT节点的特性之外 子节点的名称还具有顺序性。比如 `/node1/app0000000001``/node1/app0000000002`
- **临时顺序EPHEMERAL_SEQUENTIAL节点** 除了具备临时EPHEMERAL节点的特性之外子节点的名称还具有顺序性。
#### 3.2.2. znode 数据结构
每个 znode 由 2 部分组成:
- **stat** :状态信息
- **data** 节点存放的数据的具体内容
如下所示,我通过 get 命令来获取 根目录下的 dubbo 节点的内容。get 命令在下面会介绍到)。
```shell
[zk: 127.0.0.1:2181(CONNECTED) 6] get /dubbo
# 该数据节点关联的数据内容为空
null
# 下面是该数据节点的一些状态信息,其实就是 Stat 对象的格式化输出
cZxid = 0x2
ctime = Tue Nov 27 11:05:34 CST 2018
mZxid = 0x2
mtime = Tue Nov 27 11:05:34 CST 2018
pZxid = 0x3
cversion = 1
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 0
numChildren = 1
```
Stat 类中包含了一个数据节点的所有状态信息的字段,包括事务 ID-cZxid、节点创建时间-ctime 和子节点个数-numChildren 等等。
下面我们来看一下每个 znode 状态信息究竟代表的是什么吧!(下面的内容来源于《从 Paxos 到 ZooKeeper 分布式一致性原理与实践》,因为 Guide 确实也不是特别清楚,要学会参考资料的嘛!
| znode 状态信息 | 解释 |
| -------------- | ------------------------------------------------------------ |
| cZxid | create ZXID即该数据节点被创建时的事务 id |
| ctime | create time即该节点的创建时间 |
| mZxid | modified ZXID即该节点最终一次更新时的事务 id |
| mtime | modified time即该节点最后一次的更新时间 |
| pZxid | 该节点的子节点列表最后一次修改时的事务 id只有子节点列表变更才会更新 pZxid子节点内容变更不会更新 |
| cversion | 子节点版本号,当前节点的子节点每次变化时值增加 1 |
| dataVersion | 数据节点内容版本号,节点创建时为 0每更新一次节点内容(不管内容有无变化)该版本号的值增加 1 |
| aclVersion | 节点的 ACL 版本号,表示该节点 ACL 信息变更次数 |
| ephemeralOwner | 创建该临时节点的会话的 sessionId如果当前节点为持久节点则 ephemeralOwner=0 |
| dataLength | 数据节点内容长度 |
| numChildren | 当前节点的子节点个数 |
### 3.3. 版本version
在前面我们已经提到ZooKeeper 的每个 znode 上都会存储数据,对应于每个 znodeZooKeeper 都会为其维护一个叫作 **Stat** 的数据结构Stat 中记录了这个 znode 的三个数据版本:
- **dataVersion** :当前 znode 节点的版本号
- **cversion** 当前 znode 子节点的版本
- **aclVersion** 当前 znode 的 ACL 的版本。
### 3.4. ACL权限控制
ZooKeeper 采用 ACLAccessControlLists策略来进行权限控制类似于 UNIX 文件系统的权限控制。
对于 znode 操作的权限ZooKeeper 提供了以下 5 种:
- **CREATE** : 能创建子节点
- **READ** :能获取节点数据和列出其子节点
- **WRITE** : 能设置/更新节点数据
- **DELETE** : 能删除子节点
- **ADMIN** : 能设置节点 ACL 的权限
其中尤其需要注意的是,**CREATE** 和 **DELETE** 这两种权限都是针对 **子节点** 的权限控制。
对于身份认证,提供了以下几种方式:
- **world** 默认方式,所有用户都可无条件访问。
- **auth** :不使用任何 id代表任何已认证的用户。
- **digest** :用户名:密码认证方式: _username:password_
- **ip** : 对指定 ip 进行限制。
### 3.5. Watcher事件监听器
Watcher事件监听器是 ZooKeeper 中的一个很重要的特性。ZooKeeper 允许用户在指定节点上注册一些 Watcher并且在一些特定事件触发的时候ZooKeeper 服务端会将事件通知到感兴趣的客户端上去,该机制是 ZooKeeper 实现分布式协调服务的重要特性。
![watcher机制](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/2020-8/watcher%E6%9C%BA%E5%88%B6.jpg)
_破音非常有用的一个特性都能出小本本记好了后面用到 ZooKeeper 基本离不开 Watcher事件监听器机制。_
### 3.6. 会话Session
Session 可以看作是 ZooKeeper 服务器与客户端的之间的一个 TCP 长连接,通过这个连接,客户端能够通过心跳检测与服务器保持有效的会话,也能够向 ZooKeeper 服务器发送请求并接受响应,同时还能够通过该连接接收来自服务器的 Watcher 事件通知。
Session 有一个属性叫做:`sessionTimeout` `sessionTimeout` 代表会话的超时时间。当由于服务器压力太大、网络故障或是客户端主动断开连接等各种原因导致客户端连接断开时,只要在`sessionTimeout`规定的时间内能够重新连接上集群中任意一台服务器,那么之前创建的会话仍然有效。
另外,在为客户端创建会话之前,服务端首先会为每个客户端都分配一个 `sessionID`。由于 `sessionID`是 ZooKeeper 会话的一个重要标识,许多与会话相关的运行机制都是基于这个 `sessionID` 的,因此,无论是哪台服务器为客户端分配的 `sessionID`,都务必保证全局唯一。
## 4. ZooKeeper 集群
为了保证高可用,最好是以集群形态来部署 ZooKeeper这样只要集群中大部分机器是可用的能够容忍一定的机器故障那么 ZooKeeper 本身仍然是可用的。通常 3 台服务器就可以构成一个 ZooKeeper 集群了。ZooKeeper 官方提供的架构图就是一个 ZooKeeper 集群整体对外提供服务。
![](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-9-10/68900686.jpg)
上图中每一个 Server 代表一个安装 ZooKeeper 服务的服务器。组成 ZooKeeper 服务的服务器都会在内存中维护当前的服务器状态,并且每台服务器之间都互相保持着通信。集群间通过 ZAB 协议ZooKeeper Atomic Broadcast来保持数据的一致性。
**最典型集群模式: Master/Slave 模式(主备模式)**。在这种模式中,通常 Master 服务器作为主服务器提供写服务,其他的 Slave 服务器从服务器通过异步复制的方式获取 Master 服务器最新的数据提供读服务。
### 4.1. ZooKeeper 集群角色
但是,在 ZooKeeper 中没有选择传统的 Master/Slave 概念,而是引入了 Leader、Follower 和 Observer 三种角色。如下图所示
![](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-9-10/89602762.jpg)
ZooKeeper 集群中的所有机器通过一个 **Leader 选举过程** 来选定一台称为 “**Leader**” 的机器Leader 既可以为客户端提供写服务又能提供读服务。除了 Leader 外,**Follower** 和 **Observer** 都只能提供读服务。Follower 和 Observer 唯一的区别在于 Observer 机器不参与 Leader 的选举过程,也不参与写操作的“过半写成功”策略,因此 Observer 机器可以在不影响写性能的情况下提升集群的读性能。
| 角色 | 说明 |
| -------- | ------------------------------------------------------------ |
| Leader | 为客户端提供读和写的服务,负责投票的发起和决议,更新系统状态。 |
| Follower | 为客户端提供读服务,如果是写服务则转发给 Leader。在选举过程中参与投票。 |
| Observer | 为客户端提供读服务器,如果是写服务则转发给 Leader。不参与选举过程中的投票也不参与“过半写成功”策略。在不影响写性能的情况下提升集群的读性能。此角色于 ZooKeeper3.3 系列新增的角色。 |
当 Leader 服务器出现网络中断、崩溃退出与重启等异常情况时,就会进入 Leader 选举过程,这个过程会选举产生新的 Leader 服务器。
这个过程大致是这样的:
1. **Leader election选举阶段**:节点在一开始都处于选举阶段,只要有一个节点得到超半数节点的票数,它就可以当选准 leader。
2. **Discovery发现阶段** 在这个阶段followers 跟准 leader 进行通信,同步 followers 最近接收的事务提议。
3. **Synchronization同步阶段** :同步阶段主要是利用 leader 前一阶段获得的最新提议历史,同步集群中所有的副本。同步完成之后
准 leader 才会成为真正的 leader。
4. **Broadcast广播阶段** :到了这个阶段ZooKeeper 集群才能正式对外提供事务服务,并且 leader 可以进行消息广播。同时如果有新的节点加入,还需要对新节点进行同步。
### 4.2. ZooKeeper 集群中的服务器状态
- **LOOKING** :寻找 Leader。
- **LEADING** Leader 状态,对应的节点为 Leader。
- **FOLLOWING** Follower 状态,对应的节点为 Follower。
- **OBSERVING** Observer 状态,对应节点为 Observer该节点不参与 Leader 选举。
### 4.3. ZooKeeper 集群为啥最好奇数台?
ZooKeeper 集群在宕掉几个 ZooKeeper 服务器之后,如果剩下的 ZooKeeper 服务器个数大于宕掉的个数的话整个 ZooKeeper 才依然可用。假如我们的集群中有 n 台 ZooKeeper 服务器,那么也就是剩下的服务数必须大于 n/2。先说一下结论2n 和 2n-1 的容忍度是一样的,都是 n-1大家可以先自己仔细想一想这应该是一个很简单的数学问题了。
比如假如我们有 3 台,那么最大允许宕掉 1 台 ZooKeeper 服务器,如果我们有 4 台的的时候也同样只允许宕掉 1 台。
假如我们有 5 台,那么最大允许宕掉 2 台 ZooKeeper 服务器,如果我们有 6 台的的时候也同样只允许宕掉 2 台。
综上,何必增加那一个不必要的 ZooKeeper 呢?
## 5. ZAB 协议和Paxos 算法
Paxos 算法应该可以说是 ZooKeeper 的灵魂了。但是ZooKeeper 并没有完全采用 Paxos算法 ,而是使用 ZAB 协议作为其保证数据一致性的核心算法。另外在ZooKeeper的官方文档中也指出ZAB协议并不像 Paxos 算法那样是一种通用的分布式一致性算法它是一种特别为Zookeeper设计的崩溃可恢复的原子消息广播算法。
### 5.1. ZAB 协议介绍
ZABZooKeeper Atomic Broadcast 原子广播) 协议是为分布式协调服务 ZooKeeper 专门设计的一种支持崩溃恢复的原子广播协议。 在 ZooKeeper 中,主要依赖 ZAB 协议来实现分布式数据一致性基于该协议ZooKeeper 实现了一种主备模式的系统架构来保持集群中各个副本之间的数据一致性。
### 5.2. ZAB 协议两种基本的模式:崩溃恢复和消息广播
ZAB 协议包括两种基本的模式,分别是
- **崩溃恢复** :当整个服务框架在启动过程中,或是当 Leader 服务器出现网络中断、崩溃退出与重启等异常情况时ZAB 协议就会进人恢复模式并选举产生新的Leader服务器。当选举产生了新的 Leader 服务器同时集群中已经有过半的机器与该Leader服务器完成了状态同步之后ZAB协议就会退出恢复模式。其中**所谓的状态同步是指数据同步用来保证集群中存在过半的机器能够和Leader服务器的数据状态保持一致**。
- **消息广播** **当集群中已经有过半的Follower服务器完成了和Leader服务器的状态同步那么整个服务框架就可以进人消息广播模式了。** 当一台同样遵守ZAB协议的服务器启动后加人到集群中时如果此时集群中已经存在一个Leader服务器在负责进行消息广播那么新加人的服务器就会自觉地进人数据恢复模式找到Leader所在的服务器并与其进行数据同步然后一起参与到消息广播流程中去。
关于 **ZAB 协议&Paxos算法** 需要讲和理解的东西太多了,说实话,笔主到现在不太清楚这俩兄弟的具体原理和实现过程。推荐阅读下面两篇文章:
- [图解 Paxos 一致性协议](http://codemacro.com/2014/10/15/explain-poxos/)
- [Zookeeper ZAB 协议分析](https://dbaplus.cn/news-141-1875-1.html)
## 6. 总结
1. ZooKeeper 本身就是一个分布式程序只要半数以上节点存活ZooKeeper 就能正常服务)。
2. 为了保证高可用,最好是以集群形态来部署 ZooKeeper这样只要集群中大部分机器是可用的能够容忍一定的机器故障那么 ZooKeeper 本身仍然是可用的。
3. ZooKeeper 将数据保存在内存中,这也就保证了 高吞吐量和低延迟(但是内存限制了能够存储的容量不太大,此限制也是保持 znode 中存储的数据量较小的进一步原因)。
4. ZooKeeper 是高性能的。 在“读”多于“写”的应用程序中尤其地明显,因为“写”会导致所有的服务器间同步状态。(“读”多于“写”是协调服务的典型场景。)
5. ZooKeeper 有临时节点的概念。 当创建临时节点的客户端会话一直保持活动,瞬时节点就一直存在。而当会话终结时,瞬时节点被删除。持久节点是指一旦这个 znode 被创建了,除非主动进行 znode 的移除操作,否则这个 znode 将一直保存在 ZooKeeper 上。
6. ZooKeeper 底层其实只提供了两个功能:① 管理(存储、读取)用户程序提交的数据;② 为用户程序提供数据节点监听服务。
## 7. 参考
1. 《从 Paxos 到 ZooKeeper 分布式一致性原理与实践》