zookeeper学习笔记

Zookeeper是一个用于分布式协调的开源框架,提供命名服务、配置管理、分布式锁等功能。其数据模型包括持久节点和临时节点,支持原子性的操作。客户端可以使用命令行或Java API进行节点的增删改查,同时支持Watch事件监听。在集群部署中,Zookeeper通过myid文件和zoo.cfg配置实现节点间的通信和故障转移。
摘要由CSDN通过智能技术生成

1.Zookeeper概念

Zookeeper 是一个分布式协调服务的开源框架。 主要用来解决分布式集群中应用系统的一致性问题,例如怎样避免同时操作同一数据造成脏读的问题。
ZooKeeper 本质上是一个分布式的小文件存储系统。 提供基于类似于文件系统的目录树方式的数据存储,并且可以对树中的节点进行有效管理。从而用来维护和监控你存储的数据的状态变化。通过监控这些数据状态的变化,从而可以达到基于数据的集群管理。

诸如:

  • 统一命名服务
  • 分布式配置管理
  • 分布式消息队列
  • 分布式锁(编码实现可以)
  • 分布式协调等功能

2.Zookeeper命令操作

2.1数据模型

2.1.1数据结构

在这里插入图片描述

  1. Znode 兼具文件和目录两种特点。既像文件一样维护着数据、元信息、 ACL、时间戳等数据结构,又像目录一样可以作为路径标识的一部分,并可以具有子 Znode。用户对 Znode 具有增、删、改、查等操作(权限允许的情况下)。
  2. Znode 具有原子性操作, 读操作将获取与节点相关的所有数据,写操作也将替换掉节点的所有数据。另外,每一个节点都拥有自己的 ACL(访问控制列表),这个列表规定了用户的权限,即限定了特定用户对目标节点可以执行的
    操作。
  3. Znode 存储数据大小有限制。 ZooKeeper 虽然可以关联一些数据,但并没有被设计为常规的数据库或者大数据存储,相反的是,它用来管理调度数据,比如分布式应用中的配置文件信息、状态信息、汇集位置等等。这些数据的共同特性就是它们都是很小的数据, 通常以 KB 为大小单位。 ZooKeeper 的服务器和客户端都被设计为严格检查并限制每个 Znode 的数据大小至多 1M,当时常规使用中应该远小于此值。
  4. Znode 通过路径引用, 如同 Unix 中的文件路径。 路径必须是绝对的,因此他们必须由斜杠字符来开头。除此以外,他们必须是唯一的,也就是说每一个路径只有一个表示,因此这些路径不能改变。在ZooKeeper 中,路径由Unicode 字符串组成,并且有一些限制。字符串"/zookeeper"用以保存管理信息,比如关键配额信息。

2.1.2节点类型

在这里插入图片描述

Znode 有两种,分别为临时节点永久节点
节点的类型在创建时即被确定,并且不能改变。

  • 临时节点:该节点的生命周期依赖于创建它们的会话。一旦会话结束,临时节点将被自动删除,当然可以也可以手动删除。 临时节点不允许拥有子节点。

  • 永久节点:该节点的生命周期不依赖于会话,并且只有在客户端显示执行删除操作的时候,他们才能被删除。

  • Znode 还有一个序列化的特性,如果创建的时候指定的话,该 Znode 的名字后面会自动追加一个不断增加的序列号。 序列号对于此节点的父节点来说是唯一的, 这样便会记录每个子节点创建的先后顺序。 它的格式为“ %10d” (10 位数字,没有数值的数位用 0 补充,例如“ 0000000001” )。

2.2服务端命令

在这里插入图片描述

2.3客户端命令-基本命令

在这里插入图片描述

服务端启动
在这里插入图片描述
客户端启动

./zkCli.sh -server localhost:2181

在这里插入图片描述
退出

quit

查看

ls /

在这里插入图片描述创建

create /appl cj

在这里插入图片描述
获取数据

get /appl

设置数据

set /appl 55

在这里插入图片描述
删除

delete /appl/55

删除全部

deleteall
help

2.4客户端命令-高级点命令

创建临时节点

create -e /app1

创建顺序节点

create -s /app1

创建临时顺序节点

create -es /app1

查看节点信息

ls -s /

3.Zookeeper JavaAPI操作

3.1Cutor介绍

在这里插入图片描述
官网

3.2Cutor API常用操作-增删改查

在这里插入图片描述

3.2.1建立连接

引入依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.jq</groupId>
    <artifactId>curator-zk</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.10</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-framework</artifactId>
            <version>4.0.0</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.21</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.21</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
package com.jq.curator;

import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.junit.Test;

public class CuratorTest {
    /**
     * 建立连接
     */
    @Test
    public void testConnect(){
        /**
         * 第一种方式
         * @param connectString 连接字符串,zk server地址和端口
         * @param sessionTimeoutMs 会话超时时间
         * @param connectionTimeoutMs  连接超时时间
         * @param retryPolicy 重试策略

        */
//        //重试策略
//        ExponentialBackoffRetry retryPolicy = new ExponentialBackoffRetry(3000,10);
//        CuratorFramework client = CuratorFrameworkFactory.newClient("192.168.56.10:2181", 60 * 1000, 15 * 1000, retryPolicy);
//        client.start();
//        //开启连接

        /**
         * 第二种方式
         * */
        ExponentialBackoffRetry retryPolicy = new ExponentialBackoffRetry(3000,10);
        CuratorFramework client = CuratorFrameworkFactory.builder()
                .connectString("192.168.56.10:2181")
                .sessionTimeoutMs(60 * 1000)
                .connectionTimeoutMs(15 * 1000)
                .retryPolicy(retryPolicy).namespace("cj").build();
        client.start();
    }
}

3.2.2创建节点

package com.jq.curator;

import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.CreateMode;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.nio.charset.StandardCharsets;

public class CuratorTest {
    private CuratorFramework client;
    /**
     * 建立连接
     */
    @Before
    public void testConnect(){
        /**
         * 第一种方式
         * @param connectString 连接字符串,zk server地址和端口
         * @param sessionTimeoutMs 会话超时时间
         * @param connectionTimeoutMs  连接超时时间
         * @param retryPolicy 重试策略

        */
//        //重试策略
//        ExponentialBackoffRetry retryPolicy = new ExponentialBackoffRetry(3000,10);
//        CuratorFramework client = CuratorFrameworkFactory.newClient("192.168.56.10:2181", 60 * 1000, 15 * 1000, retryPolicy);
//        client.start();
//        //开启连接

        /**
         * 第二种方式
         * */
        ExponentialBackoffRetry retryPolicy = new ExponentialBackoffRetry(3000,10);
        client = CuratorFrameworkFactory.builder()
                .connectString("192.168.56.10:2181")
                .sessionTimeoutMs(60 * 1000)
                .connectionTimeoutMs(15 * 1000)
                .retryPolicy(retryPolicy).namespace("cj").build();
        client.start();
    }

    /**
     * 创建节点:create 持久,临时,顺序 数据
     *  1. 基本创建
     *  2. 创建节点带有数据
     *  3.设置节点类型
     *  4.创建带有多级节点 /app1/p1
     */
    @Test
    public void testCreate() throws Exception {
        //1.基本创建
        //如果没有指定数据,默认为当前客户端的ip地址作为数据
        //String path= client.create().forPath("/app1");
        //2. 创建节点带有数据
        //String path= client.create().forPath("/app2","hello app2".getBytes());
        //3.设置节点类型
        //String path= client.create().withMode(CreateMode.EPHEMERAL).forPath("/app3");
        //4.创建带有多级节点 /app4/p1
        String path= client.create().creatingParentsIfNeeded().forPath("/app4/p1");

        System.out.println(path);
    }
    @After
    public void close(){
        if (client!=null){
            client.close();
        }
    }

}

3.2.3删除节点

    /**
     * 删除节点:delete,deleteall
     * 1. 删除单个节点
     * 2.删除带有子节点的节点
     * 3.必须成功删除
     * 4.回调
     */
    @Test
    public void testDelete() throws Exception {
        //1.删除单个节点
        client.delete().forPath("/app1");
    }
    @Test
    public void testDelete2() throws Exception {
        //2.删除带有子节点的节点
        client.delete().deletingChildrenIfNeeded().forPath("/app4");
    }
    @Test
    public void testDelete3() throws Exception {
        //3.必须成功删除
        client.delete().guaranteed().forPath("/app2");
    }
    @Test
    public void testDelete4() throws Exception {
        //4.回调
        BackgroundCallback backgroundCallback = new BackgroundCallback() {
            @Override
            public void processResult(CuratorFramework curatorFramework, CuratorEvent curatorEvent) throws Exception {
                System.out.println("我被删除了");
                System.out.println(curatorEvent);
            }
        };
        client.delete().guaranteed().inBackground(backgroundCallback).forPath("/dubbo");
    }v

3.2.4修改节点

 /**
     * 修改节点:
     * 1. 修改数据
     * 2.根据版本修改
     */
    @Test
    public void testSet() throws Exception {
        client.setData().forPath("/app1","jeee".getBytes());
    }
    @Test
    public void testSetForVersion() throws Exception {
        Stat stat = new Stat();
        client.getData().storingStatIn(stat).forPath("/app1");

        int version = stat.getVersion();
        System.out.println(version);
        client.setData().withVersion(version).forPath("/app1","jeee2".getBytes());
    }

3.2.5查询节点

    /**
     * 查询节点:
     * 1. 查询数据:get
     * 2.查询子节点: ls
     * 3. 查询节点的状态信息: ls-s
     * @throws Exception
     */
    @Test
    public void testGet1() throws Exception {
        //1. 查询数据:get
        byte[] bytes = client.getData().forPath("/app1");
        System.out.println(new String(bytes));

    }
    @Test
    public void testGet2() throws Exception {
        //2.查询子节点: ls
        List<String> path = client.getChildren().forPath("/app4");
        System.out.println(path);
    }
    @Test
    public void testGet3() throws Exception {
        //3. 查询节点的状态信息: ls-s
        Stat stat = new Stat();
        client.getData().storingStatIn(stat).forPath("/app1");
        System.out.println(stat);

    }

3.3Cutor API常用操作-Watch事件监听

在这里插入图片描述

3.3.1概念

ZooKeeper 提供了分布式数据发布/订阅功能一个典型的发布/订阅模型系统定义了一种一对多的订阅关系,能让多个订阅者同时监听某一个主题对象,当这个主题对象自身状态变化时,会通知所有订阅者,使他们能够做出相应的处理。
ZooKeeper 中,引入了 Watcher 机制实现这种分布式的通知功能
ZooKeeper 允许客户端向服务端注册一个 Watcher 监听,当服务端的一些事件触发了这个 Watcher,那么就会向指定客户端发送一个事件通知来实现分布式的通知功能。
触发事件种类很多, 如:节点创建,节点删除,节点改变,子节点改变等。
总的来说可以概括 Watcher 为以下三个过程:

  • 客户端向服务端注册 Watcher、
  • 服务端事件发生触发 Watcher、
  • 客户端回调 Watcher 得到触发事件情况
    Curator 引入了Cache来实现对Zookeeper服务端事件的监听

3.3.2Watch 机制特点

  • 一次性触发
    事件发生触发监听,一个 watcher event 就会被发送到设置监听的客户端,这种效果是一次性的, 后续再次发生同样的事件,不会再次触发。
  • 事件封装
    ZooKeeper 使用 WatchedEvent 对象来封装服务端事件并传递。WatchedEvent 包含了每一个事件的三个基本属性:通知状态(keeperState), 事件类型(EventType) 和节点路径(path)
  • event 异步发送
    watcher 的通知事件从服务端发送到客户端是异步的。
  • 先注册再触发
    Zookeeper 中的 watch 机制,必须客户端先去服务端注册监听,这样事件发送才会触发监听,通知给客户端。

3.4分布式锁

相比于redis和数据库 性能稳定可靠
分布式锁,这个主要得益于 ZooKeeper 保证了数据的强一致性
锁服务可以分为两类,一个是保持独占,另一个是控制时序。

所谓保持独占,就是所有试图来获取这个锁的客户端,最终只有一个可以成功获得这把锁。通常的做法是把 zk 上的一个 znode 看作是一把锁,通过 createznode 的方式来实现。所有客户端都去创建 /distribute_lock 节点,最终成功创建的那个客户端也即拥有了这把锁。

控制时序,就是所有试图来获取这个锁的客户端,最终都是会被安排执行,只是有个全局时序了。做法和上面基本类似,只是这/distribute_lock 已经预先存在,客户端在它下面创建临时有序节点
(这个可以通过节点的属性控制:CreateMode.EPHEMERAL_SEQUENTIAL 来指定)。 Zk 的父节点(/distribute_lock)维持一份 sequence,保证子节点创建的时序性,从而也形成了每个客户端的全局时序。
在这里插入图片描述在这里插入图片描述

3.5模拟12306售票案例

在这里插入图片描述

4.Zookeeper集群搭建

在这里插入图片描述

4.1上传zookeeper并解压

tar -zxvf apache-zookeeper-3.5.6-bin.tar.gz

4.2移动解压的文件,并重新命名

mkdir /usr/local/zookper-cluster
cp -r apache-zookeeper-3.5.6-bin /usr/local/zookper-cluster/zookper-1
cp -r apache-zookeeper-3.5.6-bin /usr/local/zookper-cluster/zookper-2
cp -r apache-zookeeper-3.5.6-bin /usr/local/zookper-cluster/zookper-3

在这里插入图片描述

4.3创建data目录,并将conf下zoo_sample.cfg改名为zoo.cfg

mkdir /usr/local/zookper-cluster/zookper-1/data
mkdir /usr/local/zookper-cluster/zookper-2/data
mkdir /usr/local/zookper-cluster/zookper-3/data
mv /usr/local/zookper-cluster/zookper-1/conf/zoo_sample.cfg /usr/local/zookper-cluster/zookper-1/conf/zoo.cfg

mv /usr/local/zookper-cluster/zookper-2/conf/zoo_sample.cfg /usr/local/zookper-cluster/zookper-2/conf/zoo.cfg

mv /usr/local/zookper-cluster/zookper-3/conf/zoo_sample.cfg /usr/local/zookper-cluster/zookper-3/conf/zoo.cfg

4.4配置每一个Zookeeper的dataDir和clientPort

分别为2181 2182 2183

修改/usr/local/zookper-cluster/zookper-1/conf/zoo.cfg

vim /usr/local/zookper-cluster/zookper-1/conf/zoo.cfg
clientPort=2181
dataDir=/usr/local/zookper-cluster/zookper-1/data

修改/usr/local/zookper-cluster/zookper-2/conf/zoo.cfg

vim /usr/local/zookper-cluster/zookper-2/conf/zoo.cfg
clientPort=2182
dataDir=/usr/local/zookper-cluster/zookper-2/data

修改/usr/local/zookper-cluster/zookper-3/conf/zoo.cfg

vim /usr/local/zookper-cluster/zookper-3/conf/zoo.cfg
clientPort=2183
dataDir=/usr/local/zookper-cluster/zookper-3/data

4.5配置集群

4.5.1在每个zookeeper的data目录下创建一个myid文件,内容分别为1,2,3这个文件 就是记录每个服务器的id

echo 1 >/usr/local/zookper-cluster/zookper-1/data/myid
echo 2 >/usr/local/zookper-cluster/zookper-2/data/myid
echo 3 >/usr/local/zookper-cluster/zookper-3/data/myid

4.5.2在每一个zookeeper的zoo.cfg配置客户端访问端口(clientPort)和集群服务器IP列表

集群服务器IP列表如下

vim /usr/local/zookper-cluster/zookper-1/conf/zoo.cfg
vim /usr/local/zookper-cluster/zookper-2/conf/zoo.cfg
vim /usr/local/zookper-cluster/zookper-3/conf/zoo.cfg

server.1=192.168.56.10:2181:3881
server.2=192.168.56.10:2182:3882
server.3=192.168.56.10:2183:3883

解释:server.服务器id=服务器IP地址:服务器之间通信端口:服务器之间投票选举端口

4.6启动集群

/usr/local/zookper-cluster/zookper-1/bin/zkServer.sh start
/usr/local/zookper-cluster/zookper-2/bin/zkServer.sh start
/usr/local/zookper-cluster/zookper-3/bin/zkServer.sh start

4.7集群角色

在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值