ZooKeeper系列-基础知识一

一、ZooKeeper简介

1.1.什么是ZooKeeper

ZooKeeper: A Distributed Coordination Service for Distributed Applications

ZooKeeper is a distributed, open-source coordination service for distributed applications. It exposes a simple set of primitives that distributed applications can build upon to implement higher level services for synchronization, configuration maintenance, and groups and naming. It is designed to be easy to program to, and uses a data model styled after the familiar directory tree structure of file systems.

ZooKeeper:一个面向分布式应用的分布式协同服务.

ZooKeeper是一个面向分布式应用的、开源的分布式协同服务.它暴露了一组基础原语集供分布式应用实现更高层次的同步、配置管理、分组、命名等服务.它被设计的易编程,并且数据模型类似文件系统的树形目录.

1.2.设计目标

ZooKeeper是简单的

  1. ZooKeeper允许分布式进程通过共享的分层名称空间相互协调,该名称空间的组织方式类似于标准的文件系统.
  2. 名称空间由数据节点znode组成,在ZooKeeper中znode类似文件系统中的文件和文件夹.
  3. 与文件系统不同的是,ZooKeeper的数据存储在内存中,这意味着ZooKeeper可以实现高吞吐、低延迟.
  4. ZooKeeper使高性能、高可用、严格有序的访问成为可能
  5. Zookeeper的高性能意味着它适用于大型分布式系统,可靠性使它避免单点故障,严格有序使得客户端可以实现复杂的同步原语.

ZooKeeper是可复制的

组成ZooKeeper服务的服务器之间是相互通讯的.它们维护了状态的内存镜像,以及将事物日志和快照持久化.

客户端通过TCP连接到一个ZooKeeper服务器,并维持这个TCP连接,通过这个连接发送请求、获取相应、获取监控事件、发送心跳.

如果TCP连接断开,则客户端将会连接到其他服务器上.

ZooKeeper是有序的

ZooKeeper会给每个处理事务添加一个数字,标记当前事务的顺序.后续操作可以利用这个有序性来实现更好层次的抽象,如同步.

ZooKeeper是快速的

在以读为主的应用中,ZooKeeper是非常快的.在数千台服务器上部署的ZooKeeper应用并且在读比写比较多的情况下,它的性能最好,比率大概在10:1.

1.3.应用场景

典型应用场景:

  1. 配置管理
  2. DNS服务
  3. 组成员管理
  4. 各种分布式锁

ZooKeeper适用于存储和协同相关的关键数据,不适合于大数据量存储.

二、ZooKeeper的数据模型

2.1.ZooKeeper的数据空间

ZooKeeper的命名空间就像标准的文件系统.空间中的一个节点,就是一个路径,以/分割.

如下图所示:
在这里插入图片描述

ZooKeeper的层次模型称作data tree. Data tree的每个节点叫做znode.

znode有如下特点:

  1. 不同于文件系统,每个节点都可以保存数据.每个节点都有一个版本号.版本从0开始.
  2. znode的数据只支持全量写入和读取.
  3. Data tree的所有API操作都是wait-free的.正在执行中的API调用不会影响其他API的完成.

2.2.ZooKeeper的节点

2.2.1.节点的类型

ZooKeeper的节点类型分为以下几种:

  1. 持久的
  2. 持久有序的
  3. 临时
  4. 临时有序的
  5. 3.6.0版本中新增三种:
    1. Container:容器性节点
    2. PERSISTENT_WITH_TTL:带ttl属性的持久节点
    3. PERSISTENT_SEQUENTIAL_WITH_TTL: 带ttl的持久有序节点

持久节点和临时节点

  1. 持久节点必须通过delete操作来删除,临时节点会在创建该节点的客户端崩溃或关闭与zookeeper的连接时,自动删除
  2. 临时节点不能拥有子节点
  3. 临时节点也可以由其他客户端连接删除

有序节点

一个节点可以被设置为有序节点,一个有序节点会被分配唯一个单调递增的整数.当创建有序节点时,一个序号会被追加到路径之后.

如下:

[zk: 127.0.0.1:2181(CONNECTED) 4] create -se /project/master-
Created /project/master-0000000002

container类型节点

container类型节点是从3.6.0之后添加的,是针对特殊应用场景如leader、锁时使用.

如果container类型的子节点都被删除,那么当前container节点也会被zookeeper服务在不久后删除.

TTL节点

TTL类型节点也是从3.6.0之后添加的,该类节点不会在客户端断开连接后立刻被删除,而是会等待设置的TTL时间后被删除.

2.2.2.watch

每一个节点在创建时都可以设置一个Watcher,用来监控当前节点的变化.

new Watcher() {
  @Override
  public void process(WatchedEvent event) {

  }
}

WatchedEvent分为两部分信息:

  1. 连接状态,常用状态如下
    1. 断开连接(Disconnected)
    2. 处于连接状态(SyncConnected)
    3. 授权失败(AuthFailed)
    4. 连接到只读server(ConnectedReadOnly)
    5. 会话过期(Expired)
    6. 关闭(Closed),不会有服务端产生,而是有本地客户端产生
  2. 节点变化状态
    1. 节点创建(NodeCreated)
    2. 节点删除(NodeDeleted)
    3. 节点数据变化(NodeDataChanged)
    4. 子节点变化(NodeChildrenChanged)
    5. Watcher删除(DataWatchRemoved)
    6. 子节点watcher删除(ChildWatchRemoved)

2.2.3.获取数据

节点数据的读写都是原子的.

节点数据的读写都有ACL控制.

2.2.4.节点信息

每个节点都会由ZooKeeper维护以下信息:

  • czxid :创建该节点的事务id
  • mzxid :最后一次修改的事务id
  • pzxid : 最后一次修改子节点的事务id
  • ctime :创建时间(毫秒)
  • mtime 最后修改时间(毫秒)
  • version 数据修改的版本号(次数),从0开始
  • cversion 对子节点修改的版本号(次数)
  • aversion 对ACL进行更改的版本号(次数)
  • ephemeralOwner 如果是临时节点,则为当前session ID,否则为0
  • dataLength 当前节点数据字段长度
  • numChildren 当前节点子节点数量

例子:

[zk: 127.0.0.1:2181(CONNECTED) 5] stat /project
cZxid = 0x6
ctime = Sat May 14 03:15:36 EDT 2022
mZxid = 0x6
mtime = Sat May 14 03:15:36 EDT 2022
pZxid = 0xd
cversion = 4
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 5
numChildren = 2
[zk: 127.0.0.1:2181(CONNECTED) 6] ls /project
[master-0000000001, master-0000000002]
[zk: 127.0.0.1:2181(CONNECTED) 7] 

三、监视点Watcher

3.1.Watcher的作用

客户端可以对指定的znode节点注册一个通知请求,在znode发生变化时就会收到一个单词的通知.

Watcher的特点:

  1. 单次的:一个watcher只会被服务端触发一次,要想继续监控,必须重新注册
  2. 单向的:变化通知由服务端向客户端发送,不会有回复
  3. watcher分为两类:节点数据监控和子节点监控

单次触发是否会丢失事件?

答案是肯定的,但是这个不会是问题.因为在接收通知和注册新的监视点之间的变化情况,我们可以通过读取ZooKeeper的状态信息来获得.

如何设置Watcher?

  1. getData(path)/exists(path)可以设置当前path的watcher
  2. getChildren(path)可以设置当前path下子节点的watcher

注意:一旦设置成功watcher就无法移除,除非:触发这个监视点或者会话关闭或过期

监视点的羊群效应

当znode节点发生变化时,ZooKeeper会触发该znode节点的所有watcher集合.如果有1000个客户端通过exists操作监视这个znode节点,那么该znode变化后就会发送1000个通知,因而被监视的znode节点的一个变化会产生一个尖峰的通知,该尖峰可能会带来影响.

可能的话,避免一个节点设置大量的监视点.

五、访问控制

ZooKeeper通过访问控制表ACL来控制对节点的访问.

权限信息操作

查看权限:getAcl

[zk: 127.0.0.1:2181(CONNECTED) 24] getAcl /project/nodes
'digest,'user:6DY5WhzOfGsWQ1XFuIyzxkpwdPo=
: cdrwa

设置权限:

setAcl:

setAcl /project/nodes digest:user:6DY5WhzOfGsWQ1XFuIyzxkpwdPo=:cdra

输入授权信息:

addAuth :

[zk: 127.0.0.1:2181(CONNECTED) 22] addauth digest user:123456

ACL构成说明:

ZooKeeper的ACL通过[scheme​ :id : permissions]来构成权限列表

  1. Scheme: 代表某种权限机制,有以下几种选择
    1. world: 默认方式,相当于全部可以访问
    2. auth: 代表认证过的用户可以访问
    3. digest: 用户名:密码认证
    4. ip: 使用ip
  2. Id:代表允许访问的用户
  3. Permissions: 权限组合字符串,有cdrwa组成,含义如下:
    1. c: create 创建
    2. d: delete 删除
    3. r: read 读取
    4. w: write 写
    5. a:admin 管理权限

java客户端对ACL的实现

以digest方式为例:

新增节点:

//构造acl信息
private List<ACL> getAcls(){
        ACL acl = new ACL();
        Id id = new Id("digest","user:"+encode("user:123456"));
        acl.setId(id);
        acl.setPerms(ZooDefs.Perms.ALL);

        List<ACL> acls = new ArrayList<>();
        acls.add(acl);
        return acls;
}
 public String encode(String str){
   byte digest[] = new byte[0];
   try {
     digest = MessageDigest.getInstance("SHA1").digest(str.getBytes());
   } catch (NoSuchAlgorithmException e) {
    e.printStackTrace();
   }
   Base64 base64 = new Base64();
   String encodeToString = base64.encodeToString(digest);
   return encodeToString;
 }    
    
   @Override
    public void createNode(String path) {
        String data = "jason";
        try {
          //创建节点时,设置acl信息
            zooKeeper.create(path, data.getBytes(), getAcls(), CreateMode.PERSISTENT);
        } catch (KeeperException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }    
    
    

获取节点数据:

@Override
    public String getNodeData(String path) {
        CountDownLatch latch = new CountDownLatch(1);
        final String[] result = {""};
        //请求时添加授权信息
        zooKeeper.addAuthInfo("digest","user:123456".getBytes());
        zooKeeper.getData(path, new Watcher() {
                    @Override
                    public void process(WatchedEvent event) {
                        System.out.println(event.getType());
                        try {
                            zooKeeper.exists(event.getPath(),this);
                        } catch (KeeperException e) {
                            e.printStackTrace();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                },
                new AsyncCallback.DataCallback() {
                    @Override
                    public void processResult(int rc, String path, Object ctx, byte[] data, Stat stat) {
                        if(data==null){
                            return;
                        }
                        System.out.println("result:"+new String(data));
                        result[0] = new String(data);
                        latch.countDown();
                    }
                },
                null);
        try {
            latch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return result[0];
    }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序猿老徐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值