一文读懂zookeeper技术

一、zookeeper的安装

1.下载linux环境zookeeper安装包(以3.6.3版本为例)

下载地址: zookeeper3.6.3
2.上传到服务器再解压:

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

3.修改安装目录名:

mv apache-zookeeper-3.6.3-bin zookeeper-3.6.3

4.修改配置文件名称:

mv  ./conf/zoo_sample.cfg zoo.cfg

注意这里可以修改配置文件相关参数的:
在这里插入图片描述

5.在bin目录下启动zookeeper服务端:

./zkServer.sh start

如下图即为启动成功:
在这里插入图片描述

二、zookeeper的客户端和服务端的使用

操作zookeeper server端有两种方式,通过客户端连接或者java api代码实现连接:
在这里插入图片描述

1.服务端常用命令:

./zkServer.sh start   //启动zk
./zkServer.sh status  //zk状态
./zkServer.sh stop   //关闭zk
./zkServer.sh restart //重启zk

2.客户端命令:

./zkcli.sh    //不写默认连接本机
./zkcli.sh -server 127.0.0.1:2181   // 写ip端口可以连接其他机器的zk服务端
quit:   //退出客户端连接
ls /      //查看节点列表
ls /app1   //查询app1节点下的所有子节点

create /app1    //创建节点
get /app1       //获取节点数据
set /app1 123   // 设置节点数据
delete  /app1  //删除节点
deleteall /app1  //删除节点app1及以下的所有子节点

create -e  //创建临时节点
create -s  //创建顺序节点,自增的

ls -s /app1  //查看节点信息明细

二、javaAPI Curator 实现连接zookeeper服务端

curator是zookeeper的java客户端工具。官网: http://curator.apache.org

1.首先添加依赖:

 <dependency>
   <groupId>org.apache.curator</groupId>
   <artifactId>curator-framework</artifactId>
   <version>4.0.0</version>
</dependency>
<dependency>
   <groupId>org.apache.curator</groupId>
   <artifactId>curator-recipes</artifactId>
   <version>4.0.0</version>
</
dependency>

2.建立连接:

public class CuratorDemo {

    /**
     * 测试curator连接zookeeper
     *
     * @param connectString:服务端ip地址
     * @param sessionTimeOUtMs:会话超时时间
     * @param connectionTimeOutMs:连接超时时间
     * @param retryPolicy:重试策略
     */
    public static void testConnect1() {
        // 重置策略
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 10);
        //第一种方式
        CuratorFramework client = CuratorFrameworkFactory.newClient("192.168.1.201:2181", 60 * 1000, 15 * 1000, retryPolicy);
        // 开启连接
        client.start();
        System.out.println("连接成功" + client);
    }

    /**
     * 测试curator连接zookeeper
     *
     * @param connectString:服务端ip地址
     * @param sessionTimeOUtMs:会话超时时间
     * @param connectionTimeOutMs:连接超时时间
     * @param retryPolicy:重试策略
     */
    public static void testConnect2() {
        // 重置策略
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 10);
        //第二种方式,链式编写
        CuratorFramework client = CuratorFrameworkFactory.builder()
                .connectString("192.168.1.201:2181")
                .sessionTimeoutMs(60 * 1000)
                .connectionTimeoutMs(15 * 1000)
                .retryPolicy(retryPolicy)
                .namespace("zktest")  //名称空间
                .build();

        // 开启连接
        client.start();
        System.out.println("连接成功" + client);
    }

    public static void main(String[] args) {
        testConnect2();
    }
}

2.创建节点:

/**
     * 创建节点
     */
    public static void  createNode() throws Exception{
        CuratorFramework client = testConnect2();
        String path = client.create().forPath("/app1");
        System.out.println(path);

    }

2.查询节点:

 /**
     * 查询节点
     */
    public static void getNodeData() throws Exception{
        CuratorFramework client = testConnect2();
        byte[] data = client.getData().forPath("/app1");
        System.out.println(new String(data));
    }

打印结果如下:

169.254.31.90   //返回的是节点的ip地址

3.获取子节点数据:

 /**
     * 获取子节点数据
     */
    public static void getChildNodeData() throws Exception{
        CuratorFramework client = testConnect2();
        List<String> list =  client.getChildren().forPath("/");
        System.out.println(list);
    }

4.获取子节点数据:

/**
     * 获取节点的状态等详细信息
     * @throws Exception
     */
    public static void getNodeStatInfo() throws Exception{
        CuratorFramework client = testConnect2();
        Stat stat = new Stat();
        client.getData().storingStatIn(stat).forPath("/app1");
        System.out.println(stat);
    }

5.修改节点数据:

/**
     * 修改节点
     */
    public static void setNode() throws Exception{
        CuratorFramework client = testConnect2();
        client.setData().forPath("/app1","app4".getBytes());
    }

5.根据版本号修改节点数据:

 /**
     * 根据版本号修改节点
     * @throws Exception
     */
    public static void  setNodeByVersion() throws Exception{
        CuratorFramework client = testConnect2();
        Stat stat = new Stat();
        client.getData().storingStatIn(stat).forPath("/app1");
        int version = stat.getVersion();
        System.out.println(version);
        client.setData().withVersion(version).forPath("app4","app5".getBytes());
    }

6.删除节点:

/**
     * 删除节点
     * @throws Exception
     */
    public static void deleteNode() throws  Exception{
        CuratorFramework client = testConnect2();
        client.delete().forPath("/app1"); //删除节点
        client.delete().deletingChildrenIfNeeded().forPath("/app1"); //删除带有子节点的节点
        client.delete().guaranteed().forPath("/app1");//必须删除
        client.delete().guaranteed().inBackground(new BackgroundCallback() {
            @Override
            public void processResult(CuratorFramework curatorFramework, CuratorEvent curatorEvent) throws Exception {
                System.out.println("回调");
            }
        }).forPath("/app1");   //删除后回调
    }

三、watcher事件监听

zookeeper允许用户在指定的节点上注册一些watcher,并在特定事件触发的时候,zookeeper服务端会将事件通知到订阅的客户端。

zookeeper提供了三种watcher:
1.nodeCache: 只监听某一特定的节点;
2.pathChildrenCache:监听某一Znode的子节点;
3.treeCache:可以监听整个树上的节点,相当于nodeCache 和pathChildrenCache的综合。

/**
     * 1.nodeCache监听示例
     */
    public static void watcherNodeCache() throws Exception {
        // 1.获取客户端对象
        CuratorFramework client = testConnect2();
        // 2.创建监听对象
        NodeCache nodeCache = new NodeCache(client, "/app1");
        // 3,注册监听
        nodeCache.getListenable().addListener(new NodeCacheListener() {
            @Override
            public void nodeChanged() throws Exception {
                System.out.println("节点变化了。。。。。");
                // 获取更新后的节点数据
                byte[] data = nodeCache.getCurrentData().getData();
                System.out.println("获取更新后的节点数据:"+new String(data));
            }
        });
        // 4.开启监听(如果设置为true,则开启监听时加载缓存数据)
        nodeCache.start(true);

        // 测试
        while (true){

        }

    }
 /**
     * 2.pathChildrenCache监听示例
     */
    public static void watcherPathChildrenCache() throws Exception {
        // 1.获取客户端对象
        CuratorFramework client = testConnect2();
        // 2.创建监听对象
        PathChildrenCache pathChildrenCache = new PathChildrenCache(client, "/app2",true);
        // 3,注册监听
        pathChildrenCache.getListenable().addListener(new PathChildrenCacheListener() {
            @Override
            public void childEvent(CuratorFramework curatorFramework, PathChildrenCacheEvent pathChildrenCacheEvent) throws Exception {
                System.out.println("子节点变化了。。。。。");
                System.out.println(pathChildrenCacheEvent);
            }
        });
        // 4.开启监听(如果设置为true,则开启监听时加载缓存数据)

        pathChildrenCache.start();
        // 测试
        while (true){

        }

    }
 /**
     * 2.treeCache监听示例
     */
    public static void watchertreeCache() throws Exception {
        // 1.获取客户端对象
        CuratorFramework client = testConnect2();
        // 2.创建监听对象
        TreeCache treeCache = new TreeCache(client, "/app2");
        // 3,注册监听
        treeCache.getListenable().addListener(new TreeCacheListener() {
            @Override
            public void childEvent(CuratorFramework curatorFramework, TreeCacheEvent treeCacheEvent) throws Exception {
                System.out.println("节点变化了。。。。");
                System.out.println(treeCacheEvent);
            }
        });
        // 4.开启监听(如果设置为true,则开启监听时加载缓存数据)
        treeCache.start();
        // 测试
        while (true){

        }

    }

四、zookeeper实现分布式锁

在通常的单机应用中,如果有并发场景,我们通常使用sychonized或者lock解决多线程的代码同步问题,而多线程是运行在同一jvm之下,这样是没有任何问题的。

但是在分布式集群环境下,多台机器,属于多个jvm,跨jvm环境是无法通过多线程锁解决线程同步问题的,这个时候就需要引入分布式锁组件,多个jvm机器公用一把锁,来解决跨机器进程间的数据同步问题,这就是分布式锁。

分布式锁的实现方式:
1.基于缓存实现。例如redis (不可靠)
2.zookeeper实现(可靠)
3.数据库层面实现,乐观锁,悲观锁(性能低,不推荐)

zookeeper实现分布式锁的原理:
核心思想:当客户端获取锁时,则创建临时顺序节点,使用完锁后,删除节点。
原理:
客户端获取锁时,会在lock节点下创建临时顺序节点,然后再获取lock下所有的子节点,发现自己创建的节点最小,则获得锁,当不是最小时,则监听比自己小的那个节点,等被删除了则再次触发比较是否最小,若是,则获取锁,若不是,则继续监听。
在这里插入图片描述

下面用多线程售票的代码,实现分布式锁应用:

public class Ticket12306 implements Runnable {

    private int tickers = 10;// 剩余总票数

    private InterProcessMutex lock;

    public Ticket12306() {
        // 重置策略
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 10);
        //第一种方式
        CuratorFramework client = CuratorFrameworkFactory.newClient("192.168.1.201:2181", 60 * 1000, 15 * 1000, retryPolicy);
        // 开启连接
        client.start();
        System.out.println("连接成功");
        lock = new InterProcessMutex(client, "/lock");
    }

    @Override
    public void run() {
        while (true) {
            // 获取锁
            try {
                lock.acquire(3, TimeUnit.SECONDS);
                if (tickers > 0) {
                    System.out.println(Thread.currentThread() + ":" + tickers);
                    Thread.sleep(1000);
                    tickers--;
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                // 释放锁
                try {
                    lock.release();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }

    }
}

public class LockTest {
    public static void main(String[] args) {
        Ticket12306 ticket12306 = new Ticket12306();
        //创建客户端
        Thread t1 = new Thread(ticket12306,"携程");
        Thread t2 = new Thread(ticket12306,"飞猪");
        t1.start();
        t2.start();
    }
}

// 等待更新

五、zookeeper集群搭建

在这里插入图片描述
首先介绍下zookeeper中leader选举规则和机制:
1.serviceid,服务器id编号,编号越大,权重越大;
2.Zxid,数据id,服务器中存放数据越大,说明越是新的数据,权重越大;
3.在leader选举过程中,如果某台服务器获得了超过半数的选举,则可以成为leader.

下面进行集群搭建:
搭建集群有两种方式,伪集群和真集群,区别在于:
伪集群是在同一集群上安装多个,以端口区分;
真集群是在不同的ip机器上安装,端口一样,ip不一样。

我们以安装真集群为例:

1.首先上传apache-zookeeper-3.6.3-bin.tar 安装包到三台服务器上去;

2.解压: tar -zxvf apache-zookeeper-3.6.3-bin.tar

3.创建集群安装目录分别于三台服务器:
mkdir /usr/local/zookeeper-cluster-1
mkdir /usr/local/zookeeper-cluster-2
mkdir /usr/local/zookeeper-cluster-3

4.将解压的文件复制到集群目录下并修改文件夹名:
cp -r apache-zookeeper-3.6.3-bin /usr/local/zookeeper-cluster-1 
mv apache-zookeeper-3.6.3-bin zookeeper-1

cp -r apache-zookeeper-3.6.3-bin /usr/local/zookeeper-cluster-2
mv apache-zookeeper-3.6.3-bin zookeeper-2

cp -r apache-zookeeper-3.6.3-bin /usr/local/zookeeper-cluster-3
mv apache-zookeeper-3.6.3-bin zookeeper-3

5.在zookeeper-1,zookeeper-2,zookeeper-3 目录下创建data目录存放zk的数据
mkdir data

6.修改conf下的zoo_sample.cfg文件名
mv zoo_sample.cfg zoo.cfg


7.分别修改zoo.cfg配置文件中的dataDir参数
dataDir=/usr/local/zookeeper-cluster-1/zookeeper-1/data
dataDir=/usr/local/zookeeper-cluster-2/zookeeper-2/data
dataDir=/usr/local/zookeeper-cluster-3/zookeeper-3/data


到这里三台服务器的zookeeper都安装完毕了,但都是独立的,现需要配置集群:

1.在每个zookeeper/data目录下创建myid文件,内容分别为123 即服务器的id
echo 1 > /usr/loca/zookeeper-cluster-1/zookeeper-1/data/myid
echo 2 > /usr/loca/zookeeper-cluster-2/zookeeper-2/data/myid
echo 3 > /usr/loca/zookeeper-cluster-3/zookeeper-3/data/myid


2.在每个服务器的zoo.cfg文件配置客户端的访问端口和服务器的ip访问列表:
vim /usr/loca/zookeeper-cluster-1/zookeeper-1/conf/zoo.cfg

server.1=192.168.1.201:2881:3881
server.2=192.168.1.202:2881:3881
server.3=192.168.1.203:2881:3881

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


3.启动集群:
/usr/local/zookeeper-cluster-1/zookeeper-1/bin/zkServer.sh start
/usr/local/zookeeper-cluster-2/zookeeper-2/bin/zkServer.sh start
/usr/local/zookeeper-cluster-3/zookeeper-3/bin/zkServer.sh start


启动后分别看zookeeper状态:

 ./zkServer.sh status

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
一台leader,两台follower,集群启动成功。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

初夏0811

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

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

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

打赏作者

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

抵扣说明:

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

余额充值