简介
在分布式环境中,存在着大量的服务,服务与服务之间难以做到彼此协调,也不便于开发人员对服务进行维护管理,而 Zookeeper 使用它简单的结构和 API ,协调服务与服务之间的关系,让开发人员专注于应用程序的核心业务逻辑,更方便的对应用程序的服务进行管理维护。 所以 zookeeper 又被称为分布式协调服务。
Zookeeper 的应用
服务注册与发现
当我们的分布式系统增加了一个服务,我们只需要利用 Znode 和 Watcher,让它注册到 Zookeeper 中,我们就可以很方便的对这个服务进行管理;
分布式锁
为了防止在分布式环境下,服务中多个进程之间互相干扰,我们可以用 Zookeeper 的临时顺序节点实现分布式锁,对这些进程进行调度,让它们顺序执行;
配置管理
我们可以把核心的配置文件交给 Zookeeper 管理。当我们修改配置文件时,Zookeeper 就会把配置文件的信息同步到集群中的所有节点中去。
Zookeeper 的特点
- 在分布式环境下,Zookeeper 的部署方式为一主( Leader )多从( Follower )的集群方式,只要半数以上的节点(包括 Leader 节点)存活,Zookeeper 集群就能正常服务。就算是 Leader 节点挂掉了,Zookeeper 也会进行崩溃恢复,所说 Zookeeper 集群本身是高可用的;
- Zookeeper 集群的数据具有全局一致性。也就是说,无论客户端连接到 Zookeeper 集群的哪一个从节点,获取的数据都是一致的;
- 在 Zookeeper 集群节点进行数据同步更新时,要么全部成功,要么全部失败。所以 Zookeeper 的数据更新具有原子性;
- 在同一个客户端对 Zookeeper 节点进行更新请求操作时,会根据发送的顺序依次去执行;
由于 Zookeeper 能存储的数据量非常小,所以数据的同步更新也会非常快。也就可以说在一定时间段内,客户端获取的数据是实时的。
Zookeeper 和其他技术的比较
Zookeeper 与 Redis 分布式锁比较
除了 Zookeeper 可以实现分布式锁之外,我们还可以使用高性能缓存技术 Redis 来实现,我们来比较一下它们的优缺点。
- Zookeeper 和 Eukera 的比较
Eureka 是 Spring Cloud 微服务架构的分布式注册中心。
在进行比较之前,我们来了解一下 CAP 定理。什么是 CAP 定理呢?
C: 一致性。 A:可用性。 P: 分区容错性
Zookeeper 是CP系统,在保证数据一致性的同时,不能保证数据的高可用
Zookeeper Docker 集群搭建
https://cloud.tencent.com/developer/article/1680299
Springboot 连接zkClient
http://www.imooc.com/wiki/Zookeeper/zookeeperclient.html
Springboot 连接 Curator
http://www.imooc.com/wiki/Zookeeper/zookeepercurator.html
Curator 是 Netflix 公司开源的一套 Zookeeper 客户端框架,后来捐献给 Apache 成为顶级的开源项目。
Curator 和 ZkClient 同样简化了 Zookeeper 原生 API 的开发工作,而 Curator 提供了一套易用性和可读性更强的 Fluent 风格的客户端 API ,还提供了 Zookeeper 各种应用场景的抽象封装,比如:分布式锁服务、集群领导选举、共享计数器、缓存机制、分布式队列等。
Curator 相较其它 Zookeeper 客户端功能更强大,应用更广泛,使用更便捷,所以它能成为当下最流行的 Zookeeper 的 Java 客户端之一。
接下来我们就开始学习如何使用 Curator 客户端对 Zookeeper 服务进行操作。
Zookeeper 会话
Zookeeper 是一个 C/S 架构的服务,也就是 Client — Server 的形式。在我们使用 Zookeeper 时,都是使用 Zookeeper 的客户端向服务端发送请求,然后由服务端做出响应返回到客户端。在这个过程中,Zookeeper 的客户端需要与 Zookeeper 服务端建立连接,建立一个连接就是新建一个会话,那么会话的状态也就是 Zookeeper 客户端与 Zookeeper 服务端的连接状态。
Session 的结构
- Session ID : 会话的唯一标识
- TimeOut: 会话存在的时间
- isClosing: 会话是否关闭
Session 的状态
- Connecting: 正在连接
- Connected: 已连接
- Reconnecting : 重连接, 重连策略
- Reconnected : 已重连
- Close: 关闭状态
Zookeeper watch
CuratorCache (zk 3.6+, curator 5.*+)
http://www.imooc.com/wiki/Zookeeper/zookeeperwatch.html
Zookeeper watch的基本原理
在介绍 Watch 的原理之前,我们先熟悉一个概念:Zookeeper 客户端对 Znode 的写操作,也就是新增节点、更新节点、删除节点这些操作,默认会开启监听;Zookeeper 客户端对 Znode 的读操作,也就是查询节点数据、查询节点是否存在、查询子节点等操作,需要手动设置开启监听。这也是为什么在GetDataRequest 请求体中会有 watch 这个属性的原因。Watch 的运行过程分为 4 部分,分别是:客户端注册 Watch 、服务端注册 Watch、服务端触发 Watch、客户端处理回调。(增删改会自动添加监听, 查要手动)
- 客户端注册watch: 将watch 注册到CuratorCache 里,Zookeeper 客户端会把该请求标记成带有 Watch 的请求,然后把 Watch 监听器注册到 ListenerManager 中。
- 服务端注册 Watch
Zookeeper 服务端接收到 Zookeeper 客户端发送过来的请求,解析请求体,判断该请求是否带有 Watch 事件,如果有 Watch 事件,就会把 Watch 事件注册到 WatchManager 中。 - 服务端触发 Watch
Zookeeper 服务端注册完 Watch 事件后,会调用 WatchManager 的 triggerWatch 方法来触发 Watch 事件,Watch 事件完成后,向客户端发送响应。 - 客户端处理回调
Zookeeper 客户端接收到 Zookeeper 服务端的响应后,解析响应体,根据响应体的类型去 ListenerManager 中查找相对应的 Watch 监听器,然后触发监听器的回调函数。
服务端:watchManager 负责触发watch 事件, 像客户端发回响应
客户端: 调用listenerManager 处理watch 事件
Zookeeper ACL
授权模式 Scheme: 授权模式 Scheme 就是选择 Zookeeper 分配权限的方式,我们可以选择的授权方式有 6 种:
- IP : 我们可以使用 IP 或 IP 段来对授权对象进行授权;
- HOST : 我们可以使用主机名的后缀来对授权对象进行授权,如果我们设置的 host 为 imooc.com,就可以匹配 coding.imooc.com,class.imooc.com;
- Auth : 给所有的认证用户授权,需要使用 addauth digest username:password 命令添加认证用户,Zookeeper 会把密码使用 SHA-1 和 BASE64 算法进行加密;
- Digest : 使用用户名和密码的方式验证,不需要使用 addauth 添加认证用户,需要手动使用SHA-1 和 BASE64 算法对密码进行加密;
- World : 默认权限 world:anyone:cdrwa,任何人都可以对节点做任意操作。
- Super : 超级权限,授权对象可以对节点做任意操作;
授权对象 ID: 授权对象的意思就是我们要把 Scheme 设置的权限授权给谁。
- 如果我们设置的 Scheme 是 Auth ,那么授权对象就是所有使用 addauth digest username:password 命令添加认证用户,那么我们在设置 ACL 时就不需要写入授权对象了,例如:auth::crwda ;
- 如果我们设置的 Scheme 是 Digest ,那么授权对象就是用户名和被加密后的密码,例如:digest:mooc:0xEep90zjLm6hbWMlQ2BGbaQWzI=:crwda
权限信息,一共有五种权限
Auth 和 Digest 都是基于Digest 模式, 它们的区别是:
- Auth 需要在设置 ACL 之前添加认证用户,并且无需手动加密,它的授权范围是所有的使用 addauth digest username:password 命令添加的用户。
- Digest 不需要提前添加认证用户,但是需要自己完成密码的加密,而且授权范围只有当前设置的用户。
观察者模式 (NodeListener)实现watch机制
Zookeeper ACL 的原理
- 在 Zookeeper 客户端中,当我们使用 addauth digest mooc:mooc 命令来添加认证用户信息时,Zookeeper 客户端会使用 ClientCnxn 客户端类的 addAuthInfo 方法来封装请求信息,把请求类型包装成权限类请求,封装完毕后发送给服务端。
- 在 Zookeeper 服务端接收到 Zookeeper 客户端发送过来的请求后,首先解析请求头,识别请求的类型,发现客户端的请求类型是 auth 时,也就是权限类请求时,Zookeeper 会使用 scheme 来判断授权类型,找具体处理授权的实现类,在实现类中来进行权限验证,然后把认证用户保存到认证信息的集合中。
在 Zookeeper 客户端使用刚才添加的认证用户信息来进行节点操作时,向 Zookeeper 服务端发送权限请求,服务端解析完成请求类型和授权类型后,再去具体的认证信息集合中去匹配请求中携带的授权信息,匹配成功则执行具体操作,匹配不到则说明该请求无授权,返回 NoAuthException 异常信息。
Zookeeper Jute (序列化方式)
Zookeeper 采用Jute 作为序列化方式,将Java 对象转为二进制,具体参考BinaryOutputArchieve, 和 BinaryInputArchieve