Loading

Zookeeper学习

基本概念

大数据生态系统里很多组件的命名都是某种动物,例如Hadoop是🐘,hive是🐝,zookeeper就是动物园管理者,是管理大数据生态系统各组件的管理员

Zookeeper是经典的分布式数据一致性解决方案,致力于为分布式应用提供一个高性能,高可用,且具有严格顺序访问控制能力的分布式协调存储服务

应用场景

  • 维护配置信息
    Java编程经常会遇到配置项,例如数据库的user、password等,通常配置信息会放在配置文件中,再把配置文件放在服务器上。当需要修改配置信息时,要去服务器上修改对应的配置文件,但在分布式系统中很多服务器都需要使用该配置文件,因此必须保证该配置服务的高可用性和各台服务器上配置的一致性。通常会将配置文件部署在一个集群上,但一个集群涉及的服务器数量是很庞大的,如果一台台服务器逐个修改配置文件是效率很低且危险的,因此需要一种服务可以高效快速且可靠地完成配置项的更改工作。Zookeeper就可以提供这种服务,使用Zab一致性协议保证一致性。在开源消息队列Kafka中,也使用zookeeper来维护broker的信息。在dubbo中也广泛使用Zookeeper管理一些配置来实现服务治理

  • 分布式锁服务
    一个集群是一个分布式系统,由多台服务器组成。为了提高并发度和可靠性,在多台服务器运行着同一种服务。当多个服务在运行时就需要协调各服务的进度,有时候需要保证当某个服务在进行某个操作时,其他的服务都不能进行该操作,即对该操作进行加锁,如果当前机器故障,释放锁并fail over到其他机器继续执行

  • 集群管理
    Zookeeper会将服务器加入/移除的情况通知给集群中其他正常工作的服务器,以及即使调整存储和计算等任务的分配和执行等,此外Zookeeper还会对故障的服务器做出诊断并尝试修复

  • 生成分布式唯一ID
    在过去的单库单表系统中,通常使用数据库字段自带的auto_increment熟悉自动为每条记录生成一个唯一的id。但分库分表后就无法依靠该属性来标识一个唯一的记录。此时可以使用Zookeeper在分布式环境下生成全局唯一性id。每次要生成一个新id时,创建一个持久顺序结点,创建操作返回的结点序号,即为新id,然后把比自己结点小的删除

设计目标

  • 高性能
    将数据存储在内存中,直接服务于客户端的所有非事务请求,尤其适合读为主的应用场景

  • 高可用
    一般以集群方式对外提供服务,每台机器都会在内存中维护当前的服务状态,每台机器之间都保持通信。只要集群中超过一般机器都能正常工作,那么整个集群就能够正常对外服务

  • 严格顺序访问
    对于客户端的每个更新请求,zookeeper都会生成全局唯一的递增编号,这个编号反应了所有事务操作的先后顺序

数据结构

Zookeeper的数据结点可以视为树状结构(或者目录),树中各节点成为znode,一个znode可以有多个子结点。Zookeeper结点在结构上表现为树状,使用路径来定位某个znode

znode兼具文件和目录两种特点,既像文件一样维护着数据、元信息、ACL、时间戳等数据结构,又像目录一样可以作为路径标识的一部分

znode结构

  1. 结点的数据
  2. 结点的子结点
  3. 结点的状态

结点类型(在创建时被确定且不能更改)

  1. 临时结点:生命周期依赖于创建它的会话,会话结束结点将被自动删除。临时结点不允许拥有子结点
  2. 持久化结点:生命周期不依赖于会话,只有在客户端显式执行删除操作时才被删除

安装启动

安装

下载然后tar解压

去到conf目录下,复制一份zoo_sample.cfg 命名为zoo.cfg

修改dataDir为自己的目录,其他配置可以默认
image

启动

来到bin目录下

直接 sh zkServer.sh 不可以,会提示你怎么使用

image

我们要启动的话就是

sh zkServer.sh start 就可以了

image

sh zkServer.sh start-foreground是前台启动,可以看到一些日志信息

image

我们在用sh zkServer.sh status查看Zookeeper启动状态

image

关闭就很简单了sh zkServer.sh stop

image

命令行操作

查看版本

echo stat|nc localhost 2181 查看Zookeeper版本

本博客是基于Zookeeper 3.7.0的

image

create

ZooKeeper不支持创建多级,必须要有父级,才能继续创建

首先创建一级节点 zk,数据为parent

create /zk parent
image

create 命令用法

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

create 默认创建的是持久化节点

-s

-s 用于指定创建顺序节点,单独使用代表创建持久化顺序节点

image

-c

-c 用于创建container节点,当container节点的最后一个子节点被删除,这个container节点也会被删除

image

-e

-e 用于创建临时节点,临时结点在会话结束后会被删除

-t

首先要配置,可以看我这一篇博客

create -t 10000 /zk/ttlnode ttl单位为毫秒,所以上述命令创建一个过期时间为10秒的node

image

十秒后,可以看到节点消失(过期)了

image

get

先创建一个持久化节点

image

直接get /zk/testnode可以获得这个节点的数据

image

-s

-s 可以获取更详细的信息

  • cZxid 数据结点创建时的事务id
  • ctime 数据结点创建时间
  • mZxid 数据结点最后一次更新时的事务id
  • mtime 数据结点最后一次更新的时间
  • pZxid 子结点最后一次修改的事务id
  • cversion 子结点的更改次数
  • dataVersion 结点数据更改次数
  • aclVersion 结点ACL的更改次数
  • ephemeralOwner 如果是临时结点,表示会话的sessionID;如果是持久结点值为0
  • dataLength 数据内容长度
  • numChildren 子结点数

image

-w

-w设置一个监听器,当节点的数据被修改时会收到提示

get -w /zk/testnode

image

getAllChildrenNumber

得到节点所有的子节点个数(不仅仅是一级的子节点)

image

getEphemerals

得到当前session下所有的临时节点

image

set

更新数据

image

-s

更新并得到节点的具体信息

image

-v

通过CAS来更新数据,CAS的概念大家都很熟悉了,这里如果版本不对,无法更新成功

当前版本为3,而想修改的版本为2,所以不对

image

image

delete

删除某个节点

image

deleteall

删除某个路径下的全部节点以及它本身

image

ls

列出某个节点的全部子节点

image

-s

-s 表示除了列出子节点还列出节点的详细信息

image

-R

递归地展示子节点

image

-w

设置监听器,当节点的子节点发生变化时有提示

image

伪集群模式搭建

复制三份配置文件zoo1.cfg、zoo2.cfg、zoo3.cfg

image

image

修改三个位置

  • dataDir:修改每个节点的存储数据目录位置
  • clientPort:1号节点2181,2号节点2182,3号节点2183
  • 集群配置:
server.1=127.0.0.1:2888:3888
server.2=127.0.0.1:2889:3889
server.3=127.0.0.1:2890:3890

三份文件都修改成功后

还需要在data目录中配置myid文件,myid文件中存放对应节点的序号

echo '1' > /home/program/zookeeper/node1data/myid
echo '2' > /home/program/zookeeper/node2data/myid
echo '3' > /home/program/zookeeper/node3data/myid

启动

sh ./bin/zkServer.sh start ./conf/zoo1.cfg
sh ./bin/zkServer.sh start ./conf/zoo2.cfg
sh ./bin/zkServer.sh start ./conf/zoo2.cfg

查看状态

image

image

image

Java API 操作

首先引入Maven依赖

        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-framework</artifactId>
            <version>4.3.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-recipes</artifactId>
            <version>4.3.0</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.31</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>1.7.31</version>
        </dependency>

然后创建CuratorFramework

public static CuratorFramework getCurator(int sessionTimeoutMs, int connectionTimeoutMs) {
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
//        return CuratorFrameworkFactory.builder()
//                .connectString("1.2.3.4:2181")
//                .sessionTimeoutMs(sessionTimeoutMs)
//                .connectionTimeoutMs(connectionTimeoutMs)
//                .retryPolicy(retryPolicy)
//                .build();
        return CuratorFrameworkFactory.newClient("1.2.3.4:2181", sessionTimeoutMs,connectionTimeoutMs,retryPolicy);
    }

    public static CuratorFramework getCurator() {
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
        return CuratorFrameworkFactory.newClient("1.2.3.4:2181", retryPolicy);
    }

最简单的构造方法只要指定重试策略和Zookeeper的地址和端口就可以

有7中重试策略

image

创建节点

String path = client.create().forPath("/zk/curator");
System.out.println(path);
//还可以直接在forPath里添加数据
String path = client.create().forPath(path,data.getBytes());

返回节点路径
image

默认创建的是持久化节点

创建临时节点,加上WithMode就可以

client.create().withMode(CreateMode.EPHEMERAL).forPath(path);
client.create().withMode(CreateMode.EPHEMERAL).forPath(path,data.getBytes());

CreateMode这个枚举中就有所有的模式

image

递归创建节点,这个操作在CLI中不行

client.create().creatingParentsIfNeeded().forPath("/curator/test/test/test");

image

删除节点

删除一个子节点

client.delete().forPath(path);

删除节点并递归删除其子节点

client.delete().deletingChildrenIfNeeded().forPath(path);

指定版本进行删除

client.delete().withVersion(1).forPath(path);

如果此版本已经不存在,则删除异常,异常信息:Exception in thread "main" org.apache.zookeeper.KeeperException$BadVersionException: KeeperErrorCode = BadVersion for XXX

强制保证删除一个节点。只要客户端会话有效,那么Curator会在后台持续进行删除操作,直到节点删除成功。比如遇到一些网络异常的情况,此guaranteed的强制删除就会很有效果。

client.delete().guaranteed().forPath(path);

读取数据

System.out.println("普通查询");
byte[] bytes = client.getData().forPath(path);
System.out.println(new String(bytes, StandardCharsets.UTF_8));
System.out.println("带状态的查询");
Stat stat = new Stat();
client.getData().storingStatIn(stat).forPath(path);
System.out.println(stat);

image

更新数据

更新数据,如果未传入version参数,那么更新当前最新版本,如果传入version则更新指定version,如果version已经变更,则抛出异常。

// 普通更新
client.setData().forPath(path,"新内容".getBytes());
// 指定版本更新
client.setData().withVersion(1).forPath(path);

版本不一致,则抛出异常,异常信息:Exception in thread "main" org.apache.zookeeper.KeeperException$BadVersionException: KeeperErrorCode = BadVersion for XXX

监听

可以查看这篇博客,写的很详细了
Curator 的事件监听

缺少的内容

ACL
Zookeeper的分布式锁

posted @ 2021-07-02 11:26  Xianhao  阅读(127)  评论(0)    收藏  举报