zookeeper_overview

概述

zk 是一个开源的,分布式协调服务,它的目的就是为了服务于分布式应用。zk 允许分布式应用通过 zk 的节点进行相互协调,常见的有配置同步、分布式锁、微服务注册与发现等等。

zk 本身和它所要协调的分布式应用一样,也是也是在集群中相互复制,以保证 zk 的高可用性。每台服务器都需要相互了解,数据保持一致。只要大多数服务器是可用的,那么整个 zk 集群就是可用的。

zk 具有以下特性

  • 有序的
  • 高可用
  • 高吞吐
  • 低延迟

其中,zk 使用一个 zxid(事务id)来保证每个更新的先后顺序,客户端可以用这个特性来实现同步原语,也就是分布式锁,zk 的高可用是通过 zk 集群的数据一致性来保证的。高吞吐以及低延迟是因为 zk 在内存中维护一套数据映射,通过内存进行读取更新。当然 zk 还会将日志文件以及数据快照持久化,这也同样为高可用提供了一定的支持。

zookeeper_overview

zk 数据模型

znode

zk 中的每个节点都由路径标识,和标准文件系统类似,都是采用 “/” 反斜杠进行进行路径分割。

如下图所示

zookeeper_overview

znode 是我们访问的主要实体,我们需要对它有一个清晰的认识。

zk 中的每个节点都可以拥有子节点以及相关联的数据。这里的数据一般是存储分布式服务的协调数据如:状态信息、配置信息、位置信息等,因此存储在节点上的数据通常很小,在字节到千字节的范围内。

watch(监听)

zk 支持可以在指定的节点上设置监听,当节点更改后,监听会被触发以及删除,客户端会受到一个回调包,告知节点被更改了。

3.6.0 版本支持持久化监听,监听触发后可以不被删除。

数据权限(ACL)

zk 的每个节点都会有一个 acl 列表(访问控制列表)来限制谁可以做什么

临时节点

ZooKeeper 也有临时节点的概念。只要创建znode的会话是存活的,这些 znode 就会存在。当会话结束时,删除znode。由于这种行为,临时节点不允许有子节点。会话的临时列表可以使用 getEphemerals() api 检索。

序列节点–唯一命名

当创建znode时,你也可以请求ZooKeeper在路径的末尾添加一个单调递增的计数器。这个计数器对于父节点是唯一的。如创建了一个节点 /A,在 A 节点使用唯一命名,那么后续创建子节点就会用数字自动递增,如:/A/1, /A/2。这个计数器是靠父节点维护的,这里是 A 节点。

zk 中的时间

  • zxid:zk 事务 id,每次对 zk状态信息的一个更改都会收到一个事务 id,该 id 是唯一的,zk 会保证事务 id 小的在事务 id 大的之前进行更新,防止出现丢失更新的情况。
  • 版本号:对节点的每次修改都会使得版本号增加,客户端操作一个节点时会带上版本号,如果版本号不一致,那么就会操作失败。这相当于加锁了。
  • version:修改 znode 数据的次数
  • cversion:对 znode 子节点修改的次数
  • aversion:对 znode 的 acl 列表修改的次数
  • Ticks:配置文件中的一个时间,单位是毫米,zk 中的大部分时间都是以该时间为基本单位。比如会话超时,就是 2 Ticks。
  • Real time:zk 除了在创建和修改 znode 时会将时间戳放入节点的 stat 结构中,其他任何地方都不会使用现实时间。

stat 结构

zk 节点的信息由一个 stat 结构维护

包含了以下信息:

  • czxid:该节点创建时候的 zxid
  • mzxid:该节点最后变更时候的 zxid
  • pzxid:该节点的子节点最后变更时候的 zxid
  • ctime :节点创建时候的时间戳
  • mtime:节点修改时候的时间戳
  • version :znode 数据变更的次数
  • cversion :znode 子节点变更的次数
  • aversion : znode 的 acl 列表变更的次数
  • ephemeralOwner:如果该节点是一个临时节点,则为创建该节点的会话id。如果它不是一个临时节点,它将为0。
  • dataLength :该节点存储的数据的长度
  • numChildren:该节点的子节点数

会话(session)

当我们使用客户端连接 zk 服务端的时候就创建了一个会话,在建立连接的过程中,会话状态时 connecting,当通过验证,连接成功的时候,状态进入 connected,如果因为身份验证失败或者会话超时,那么,就进入一个 close 状态。

zookeeper_overview

当我们使用以下代码连接 zk 服务器成功的时候就是建立了一个会话。

ZooKeeper zooKeeper = new ZooKeeper("127.0.0.1:2181", 4000, new Watcher() {
    @Override
    public void process(WatchedEvent watchedEvent) {
        if (Event.KeeperState.SyncConnected == watchedEvent.getState()) {
            System.out.println("watch");
        }
    }
});

zk 服务器会给客户端分配一个 64 位的 session id,同时为了安全考虑,也会配套的创建一套加密密码,当客户端因某种原因。连接到其他的 zk 服务器的时候,需要将 session id 和密码一起发送给 zk 服务器,重新建立连接。

zk 有一个 session 过期时间,默认为 2 Ticks time,当超过这个时间,zk 服务器没有收到客户端的信息(包括心跳),那么就会断开连接,session 就会进入到一个 close 状态。这时候在该会话中建立的所有临时节点都会被删除,同时通知给所有监听了这些节点的客户端。

如果因为连接的 zk 服务器宕机了,或者 session 在 zk 集群中重新分区时,这时候需要与其他 zk 服务器建立 session 连接,如果在超时时间内连接上了,那么状态重新回归 connected,否则,连接过期,这时候 zk 客户端会自动处理重新连接,无需重新创建新的会话对象(new ZooKeeper() )。

会话通过客户端发送的请求保持活动。如果会话在一段时间内处于空闲状态,该会话将超时,那么客户机将发送一个PING请求以保持会话处于活动状态。这个PING请求不仅允许ZooKeeper服务器知道客户端仍然是活动的,而且它还允许客户端验证它到ZooKeeper服务器的连接仍然是活动的。PING的时间足够保守,以确保有合理的时间检测死连接并重新连接到新服务器。

监听(watch)

定义:在 zk 中监听是一次性的,当对某个节点设置了监听,那么当该节点进行了变更后,客户端就会受到一个回调通知。

所有对节点的读操作都可以设置对该节点的监听: getData(), getChildren(), 以及 exists()

zooKeeper.getData("/", new Watcher() {
    @Override
    public void process(WatchedEvent event) {
        System.out.println(event.getState());
    }
}, new Stat());

zooKeeper.getChildren("/", new Watcher() {
    @Override
    public void process(WatchedEvent event) {

    }
});

zooKeeper.exists("/", new Watcher() {
    @Override
    public void process(WatchedEvent event) {

    }
});

在 zk 3.6.0 版本中,客户端还可以在znode上设置永久的、递归的监听,这些监听在被触发时不会被删除,并递归地触发注册znode以及任何子znode上的更改。

如下,分别是创建持久监听已经持久递归监听

zooKeeper.addWatch("/",AddWatchMode.PERSISTENT);
zooKeeper.addWatch("/",AddWatchMode.PERSISTENT_RECURSIVE);

监听的一些顺序性问题:

  • 客户端在获取到节点的新数据之前,会先拿到对于该节点的监听时间。
  • 监听的顺序和 zk 更新节点的顺序是一致的

一些要注意的点:

  • 标准的监听只触发一次,触发后如果想要对对应的节点继续监听数据,需要再次对该节点添加监听机制
  • 由于标准的监听是一次性的,在获取数据和发送新的监听请求这中间可能可能有多次节点变更,这样会丢失掉一些关于该节点的更新监听

访问控制列表(ACL)

acl 支持以下几种权限

  • CREATE:可以创建子节点
  • READ:可以从节点中获取数据和子节点列表
  • WRITE:可以设置节点的数据
  • DELETE:可以删除子节点
  • *ADMIN:可以设置权限

保证

zk 为了能够构建更加复杂的服务,提供了以下保证

  • 顺序一致性:来自客户端的更新将按照发送的顺序执行
  • 原子性:更新要么成功,要么失败,没有部分成功部分失败
  • 单一系统映像:客户端在不同的 zk 服务器中看到的都是相同的视图。即使因为故障转移到其他服务器,也不会看到历史视图。
  • 可靠性:一旦节点被创建或更新,那么它将一直存在,除非它被删除或者更改了。
  • 及时性:保证客户端在一定时间内看到到最近视图

简单的 api

zk 立志于提供一套简单编程接口,因此只支持以下几种 api

  • create :创建节点
  • delete :删除节点
  • exists :判断某个节点是否存在
  • get data:读取某个节点的数据
  • set data:为某个节点写入数据
  • get children:检索节点的子节点列表
  • sync :等待数据被同步

实现

每台 zk 服务器都会复制将自己的数据复制一份存为副本。复制的信息包含整个 zk 的内存数据,更新数据被记录到磁盘以实现可恢复性,写入数据在写入内存前会先被序列化到磁盘。

zk 集群中的服务器分为 1 台 leader 和多台 follower。zk 集群中的每台服务器都为客户端提供服务,不同的是 follower 提供读服务,leader 提供读服务和写服务,当 follower 接收到写请求时会转发到 leader 服务器处理,leader 服务器写入数据完后会广播给所有的 follower 进行同步写数据。假如 leader 出现故障,那么会从 follower 中选举一个新 leader 出来。

文章为本人学习过程中的一些个人见解,漏洞是必不可少的,希望各位大佬多多指教,帮忙修复修复漏洞!!!

参考资料

zk 官网

Original: https://www.cnblogs.com/cyrus-s/p/15506553.html
Author: 三木同学
Title: zookeeper_overview

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/589515/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 写个续集,填坑来了!关于“Thread.sleep(0)这一行‘看似无用’的代码”里面留下的坑。

    你好呀,我是居家十三天只出了一次小区门的歪歪。 这篇文章是来填坑的,我以前写文章的时候也会去填之前的一些坑,但是由于拖延症,大多都会隔上上几个月。 首先非常感谢阅读我文章的朋友,同…

    Java 2023年6月5日
    072
  • 20220727-Java中方法重写override

    代码示例 注意事项 代码示例 public class OverrideExercise { public static void main(String[] args) { Pe…

    Java 2023年6月15日
    070
  • 力扣刷题-1两数求和

    自己只会暴力求解,看见大佬的进阶求解,感觉tql,题源链接 Original: https://www.cnblogs.com/chaos2022/p/16608877.htmlA…

    Java 2023年6月13日
    070
  • elasticsearch按URL查询

    排序查询:localhost:9200/get-together/_search?sort=date:asc, 排序以及按字段查询:localhost:9200/get-toget…

    Java 2023年6月7日
    073
  • springboot各层作用

    搞懂springboot各层作用 总结springboot项目流程 Springboot项目分为以下几个层: controller层:控制层,负责前后端交互,接收前端发送的请求,然…

    Java 2023年6月7日
    070
  • 数据库学习记录(三)

    1.关于查询记录的去重? mysql>select distinct job from emp;//distinct关键字去除重复记录 mysql>select ena…

    Java 2023年6月7日
    070
  • UVa 10387- Billiard

    ============= Problem A: Billiard Assume that the collisions with a side are elastic (no e…

    Java 2023年5月29日
    078
  • 队列的模拟及环形队列思路

    定义 队列是一个 有序列表,可以用 数组或是 链表来实现。 遵循 先入先出的原则。即:先存入队列的数据,要先取出。后存入的要后取出 模拟思路 队列本身是有序列表,若使用数组的结构来…

    Java 2023年6月16日
    069
  • 如何在servlet取得spring beans (autowired)(转)

    在应用中一般普通的JavaPojo都是由Spring来管理的,所以使用autowire注解来进行注入不会产生问题,但是有两个东西是例外的,一个是 Filter,一个是Servlet…

    Java 2023年5月30日
    072
  • 使用 Certbot 申请 Let’s Encrypt SSL 证书,并定时续期

    网站有个 SSL 证书,可以提高安全性、及提高搜索引擎的排名。Let’s Encrypt SSL 证书是免费的,可以用命令行申请,也可以用命令行续期。 Let&#821…

    Java 2023年6月9日
    061
  • linux 下隐藏进程的一种方法

    前言 本文所用到的工具在https://github.com/gianlucaborello/libprocesshider 可以下载 思路就是利用 LD_PRELOAD 来实现系…

    Java 2023年6月16日
    075
  • 蓝桥杯——校内模拟题目分析

    蓝桥杯——校 内模拟题目分析 (顺序有可能会有点乱,不要信上面填的答案,看解析,后面附有答案) 1 这道题就不用多说了吧,计算机的单位之间进制为2 的10 次方 所以答案为: 15…

    Java 2023年6月5日
    070
  • 就这么一个简单的校验,80%的程序员却做不到,更不理解!

    在学生管理系统里,其中会有学生信息采集的功能。程序结构不外乎下面的分层实现方式。 开发出来这个功能,我觉得大家都易如反掌了。 当然易如反掌。 OK,我要说的是数据校验,以最简单的非…

    Java 2023年6月15日
    078
  • 设计模式—组合模式

    类型:结构型 目的:将对象集合组合成 树形结构,使客户端可以以 一致的方式处理 单个对象(叶子节点)和 *组合对象(根节点) 话不多说,上优化案例。 优化案例 不使用组合模式。现有…

    Java 2023年6月7日
    0183
  • Java中的基本数据类型

    404. 抱歉,您访问的资源不存在。 可能是网址有误,或者对应的内容被删除,或者处于私有状态。 代码改变世界,联系邮箱 contact@cnblogs.com 园子的商业化努力-困…

    Java 2023年6月5日
    081
  • Java基础教程:序列化与反序列化

    Java基础教程:序列化与反序列化 序列化 Java序列化算法 所有保存到磁盘的对象都有一个序列化编码。 posted @2020-06-03 13:35 子烁爱学习 阅读(149…

    Java 2023年5月29日
    059
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球