zookeeper 进阶 —— (动态上下线监听;;分布式锁;;企业面试)(师承尚硅谷)

Zookeeper 进阶

1. 服务器动态上下监听案例

需求:服务器上下限,客户端可以监听

(需求的图片)

具体实现:
1.先启动zookeeper创建一个/servers结点:
2.上代码:
服务器编写思路

写代码具体的思路:
1.连接zk
2.创建结点(服务器上线)
3.业务逻辑(睡觉)

DirstributedServers类代码:

package com.wts.case01;
/**
 * 服务器注册到zk集群等相关操作
 */

import org.apache.zookeeper.*;

import java.io.IOException;

public class DirstributedServers {
    private String connectionString = "hadoop102:2181,hadoop103:2181,hadoop104:2181";
    private int sessionTimeout = 2000;//2s
    private ZooKeeper zk;

    public static void main(String[] args) throws IOException, InterruptedException, KeeperException {
        //1.获取zk连接
        DirstributedServers servers = new DirstributedServers();
        servers.getConnection();

        //2.注册服务器到zk集群 -- 也就是创建节点
        servers.regiter(args[0]);

        //启动业务逻辑
        servers.bussiness();
    }

    //启动业务逻辑,等待服务器运行
    private void bussiness() throws InterruptedException {
        Thread.sleep(Long.MAX_VALUE);
    }

    //注册服务器到zk集群
    private void regiter(String hostname) throws InterruptedException, KeeperException {//临时带序号结点
        String create = zk.create("/servers/" + hostname, hostname.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
        System.out.println(hostname + " is online....");
    }

    //连接zk
    private void getConnection() throws IOException {
        zk = new ZooKeeper(connectionString, sessionTimeout, new Watcher() {
            @Override
            public void process(WatchedEvent watchedEvent) {

            }
        });
    }
}

客户端编程思路:

1.连接zk
2.监听服务器上下线情况(监听结点)
3.业务逻辑--睡觉

DistributeClient类:

package com.wts.case01;

import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * 客户端监听zk集群
 */
public class DistributeClient {
    private String connectionString = "hadoop102:2181,hadoop103:2181,hadoop104:2181";
    private int sessionTime = 2000;
    private static ZooKeeper zk;

    public static void main(String[] args) throws IOException, InterruptedException, KeeperException {
        //1.连接zk
        DistributeClient client = new DistributeClient();
        client.getConnection();

        //2.监听/servers下面的子节点的删除和增加
        client.getServersList();

        //3.业务逻辑--也就是睡觉
        client.bussiness();
    }

    //业务逻辑--睡觉
    private void bussiness() throws InterruptedException {
        Thread.sleep(Long.MAX_VALUE);
    }

    //监听--得到List
    private void getServersList() throws InterruptedException, KeeperException {
        ArrayList<String> arrayList = new ArrayList<>();
        List<String> children = zk.getChildren("/servers", true);
        for (String child : children) {
            byte[] data = zk.getData("/servers/" + child, false, null);// 2个斜杠忘记了会报错
            arrayList.add(new String(data));
        }
        //打印
        System.out.println(arrayList);
    }

    private void getConnection() throws IOException {
        //zk快速生成了成员变量 -- alt+ctrl+f--current m
        zk = new ZooKeeper(connectionString, sessionTime, new Watcher() {
            @Override
            public void process(WatchedEvent watchedEvent) {
                //注册一次,监听一次,所以这里面再写监听 (2.写监听代码的最后一步)
                try {
                    getServersList();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (KeeperException e) {
                    e.printStackTrace();
                }
            }
        });
    }
}

其中报错记录:
org.apache.zookeeper.KeeperException$ConnectionLossException: KeeperErrorCode = Connec…
ECS的端口没设置好
查看连接ESC报错,org.apache.zookeeper.KeeperExceptionConnectionLossException: KeeperErrorCode = Connec…

效果
1.创建删除结点模拟服务器上下限,在Client客户端控制台上可以看到:

在这里插入图片描述
2.用代码上线服务器,在客户端也可以看到变化
在这里插入图片描述

2.zk分布式锁案例

需求:

在这里插入图片描述

实现:
zk的所谓的加锁,就是在特定位置创建临时带序号结点,判断最小的编号,做出相应的操作

上代码:
DistributedLock 类:

思路:
1、连接zk
2、加锁,创建临时带序号结点,判断最小的编号,做出相应的操作
(是否需要监听序号的前一个结点?)
3、解锁,删除创建的临时结点
package com.wts.case02;

import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;

import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;

public class DistributedLock {
    private final ZooKeeper zk;
    private String connectString = "hadoop102:2181,hadoop103:2181,hadoop104:2181";
    private final int sessionTimeout = 2000;

    private CountDownLatch connectLatch = new CountDownLatch(1);
    private CountDownLatch waitLatch = new CountDownLatch(1);//等待前一个步骤执行完毕之后,再执行后面的操作
    private String waitPath;//前一个结点的路径
    private String currentMode;//创建的当前节点

    public DistributedLock() throws IOException, InterruptedException, KeeperException {


        //连接zk
        zk = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
            @Override
            public void process(WatchedEvent watchedEvent) {
                //countDownLatch  ,,连接上zk后,可以释放掉了
                if (watchedEvent.getState() == Event.KeeperState.SyncConnected) {
                    //如果当前状态是连接上了,释放
                    connectLatch.countDown();
                }

                //waitLatch,,他也要释放
                if (watchedEvent.getType() == Event.EventType.NodeDeleted && watchedEvent.getPath().equals(waitPath)) {
                    waitLatch.countDown();
                }
            }
        });

        //等待zk正常连接后,往下走程序---阻塞。
        connectLatch.await();

        //判断根节点是否存在/locks是否存在
        Stat stat = zk.exists("/locks", false);
        if (stat == null) {
            //创建根节点
            zk.create("/locks", "locks".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
        }

    }

    //对zk加锁---所谓的加锁就是在里面创建一个临时节点,判断这个临时结点是否是最小的一个
    public void zkLock() {
        //创建带序号的临时结点
        try {
            currentMode = zk.create("/locks/" + "seq-", null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
            //判断当前节点是否是序号最小的临时结点,如果是,获取锁;非,监听前一个结点
            List<String> children = zk.getChildren("/locks", false);
            if (children.size() == 1) {
                return;
            } else {
                //排序
                Collections.sort(children);

                //获取结点的名称seq-000000  截取字符串的方式
                String thisNode = currentMode.substring("/locks/".length());
                //获取这个结点当前的位置
                int index = children.indexOf(thisNode);
                if (index == -1) {
                    System.out.println("数据异常,,,");
                } else if (index == 0) {
                    //当前结点再列表中是最小的,Client获取锁
                    return;
                } else {
                    //需要监听前一个结点变化
                    waitPath = "/locks/" + children.get(index - 1);
                    zk.getData(waitPath, true, null);//watch是true,就转到监听部分的代码,位置在上面的new watch()上面

                    //等待执行
                    waitLatch.await();

                    //监听到之后立马return
                    return;
                }

            }
        } catch (KeeperException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

    //对zk解锁
    public void unZkLock() {
        //解锁就是删除节点
        try {
            zk.delete(currentMode, -1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (KeeperException e) {
            e.printStackTrace();
        }
    }
}

测试代码:

package com.wts.case02;

import org.apache.zookeeper.KeeperException;

import java.io.IOException;

public class DistributedLockDemo {
    public static void main(String[] args) throws IOException, InterruptedException, KeeperException {
        //创建两个客户端
        DistributedLock lock1 = new DistributedLock();
        DistributedLock lock2 = new DistributedLock();

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    lock1.zkLock();
                    System.out.println("线程1-获取锁--");

                    Thread.sleep(2000);

                    lock1.unZkLock();
                    System.out.println("线程1-释放锁--");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();


        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    lock2.zkLock();
                    System.out.println("线程2--获取锁--");

                    Thread.sleep(5 * 1000);

                    lock2.unZkLock();
                    System.out.println("线程2--释放锁--");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}

效果:
在这里插入图片描述

3. 企业面试

1.选举机制
半数机制,超过半数的投票通过,即通过。
(1) 第一次启动选举规则:
投票过半数时, 服务器 id 大的胜出
(2) 第二次启动选举规则:
①EPOCH 大的直接胜出
②EPOCH 相同, 事务 id 大的胜出
③事务 id 相同,服务器 id 大的胜出

2 .生产集群安装多少 zk 合适?
安装奇数台。
生产经验:

  • 0 台服务器: 3 台 zk;

  • 20 台服务器: 5 台 zk;

  • 100 台服务器: 11 台 zk;

  • 200 台服务器: 11 台 zk

服务器台数多: 好处,提高可靠性;
坏处:提高通信延时

3.常用命令
ls 、get 、create 、delete


zookeeper源码分析。。。过几年再学

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值