什么是zookeeper

zookeeper是一个开源的分布式,基于观察者模式的分布式服务管理框架。

使用场景

统一配置管理、统一命名服务、统一集群管理、软负载均衡、服务器节点动态上下线等

统一配置管理:将配置信息写入zookeeper的一个znode中,各个服务端监听这个znode,znode中内容经过修 改,zookeeper将会通知各个服务器

统一命名服务:对服务/应用进行统一命名,便于识别

统一集群管理:跟配置管理一样,写入znode并且监听,获取实时变化

软负载均衡: 记录每台服务器的访问量,让访问少的去处理新的请求

服务器节点动态上下线:客户端实时洞察服务器上下线变化

zookeeper特点

1、zookeeper有两种角色:主节点(Leader),一群从节点(Follower)

2、集群中只有要一半以上的节点存活,zookeeper集群就能正常服务

3、数据一致性,客户端无论连接到哪个服务器,数据都是一致的

4、实时性,在一定时间范围之内,客户端能读取到最新的数据

5、原子性,更新数据要么全成功,要么失败

6、来自于同一个客户端的更新请求,按照其顺序依次执行

zookeeper工作机制
在这里插入图片描述

安装zookeeper

1、将zookeeper的tar包放到liunx系统下

2、解压:

tar -zxvf zookeeper-3.4.10.tar.gz 	

3、修改配置文件(zookeeper包conf目录下):

mv zoo_sample.cfg  zoo.cfg

4、打开zoo.cfg文件修改dataDir路径,zookeeper所在的包路径下+/zkData
在这里插入图片描述

5、在zookeeper所在的包路径下创建zkData

mkdir zkData
#启动zookeeper(zookeeper的bin目录下)
./zkServer.sh start
#查看状态
./zkServer.sh status
#启动客户端
./zkCli.sh
#退出客户端
quit
#停止zookeeper
./zkServer.sh stop

zookeeper集群

1、在zkData目录下创建一个myid的文件:touch myid

2、在myid中添加对应的编号 : 1

3、剩下的服务器按照上面的顺序搭, 不过另外两台服务器的myid内容为 2 、 3

4、进入zoo.cfg文件,修改dataDir路径,以及添加如下配置

server.1=第一台服务器的主机名:2888:3888
server.2=第二台服务器的主机名:2888:3888
server.3=第三台服务器的主机名:2888:3888

zookeeper命令

#查看当前znode内容
ls /
#查看当前节点详细数据
ls2 /
#创建节点
create /zg "zg"
create /zg/bj "bj"
#创建短暂节点
create -e /zg/sh "sh"
#创建序号节点
create -s /zg/sz "sz"
#获取节点值
get /zg
#修改节点
set /zg/bj "sdbj"
#删除节点
delete /zg/jiujiuliu
#递归删除节点
rmr /zg/jiaban
#查看节点状态
stat /zg

API

添加pom文件

	<dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-core</artifactId>
      <version>2.8.2</version>
    </dependency>
    <dependency>
      <groupId>org.apache.zookeeper</groupId>
      <artifactId>zookeeper</artifactId>
      <version>对应的zookeeper版本号</version>
    </dependency>

log4j.properties

log4j.rootLogger=INFO, stdout 
log4j.appender.stdout=org.apache.log4j.ConsoleAppender 
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] 
- %m%n 
log4j.appender.logfile=org.apache.log4j.FileAppender 
log4j.appender.logfile.File=target/spring.log 
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout 
log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] 
- %m%n 

创建zookeeper客户端


public static final String connectString = "主机名1:2181,主机名2:2181,主机名3:2181";

public static final Integer sessionTimeout = 2000;

public static final String parNode = "/servers";

public void serverd() throws Exception {

        zooKeeper = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
            @Override
            public void process(WatchedEvent watchedEvent) {
            }
        });
    }

添加节点

public void cli() throws KeeperException, InterruptedException {
    // 参数一:路径   参数二:值  参数三:节点权限   参数四:节点类型
        String s = zooKeeper.create("/m", "mengyizhou".getBytes(), 			ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
        System.out.println("s = " + s);
    }

删除节点

 public void delnode() throws KeeperException, InterruptedException {
        //参数一:路径    参数二:版本
        zooKeeper.delete("/m",0);
 }

判断节点那是否存在

 public void judge() throws KeeperException, InterruptedException {
     	// 参数一:路径   参数二:是否监听
        Stat exist = zooKeeper.exists("/meng", false);
        System.out.println(exist == null ? "exists" : "no exists");
 }

监听子节点变化

 @Before
    public void serverd() throws Exception {

        zooKeeper = new ZooKeeper(Zenum.connectString, Zenum.sessionTimeout, new Watcher() {
            @Override
            public void process(WatchedEvent watchedEvent) {
                try {
                    System.out.println("============");
                   //参数一 : 路径(获取该路径下所有子节点)   参数二:是否监听
                    List<String> list = zooKeeper.getChildren("/", true);
                    list.forEach((item)->{
                        System.out.println(item);
                    });
                    System.out.println("============");
                } catch (KeeperException e) {
                    e.printStackTrace();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
    }

 	/**
     * 监控节点变化
     * @throws KeeperException
     * @throws InterruptedException
     */
    @Test
    public void getChildrens() throws KeeperException, InterruptedException {
        Thread.sleep(5000000);
    }

创建节点

在这里插入图片描述
控制台

在这里插入图片描述

监听服务器动态上下线

//服务器
public class Server {

    public ZooKeeper zooKeeper;
    
    public static final String connectString = "主机名1:2181,主机名2:2181,主机名3:2181";

	public static final Integer sessionTimeout = 2000;

	public static final String parNode = "/servers";

    public static void main(String[] args) throws IOException, KeeperException, InterruptedException {
        //连接到zookeeper集群
        Server server = new Server();
        server.Connect();
        //注册服务器
        server.register(args[0]);
        //逻辑代码
        server.business(args[0]);
    }

    /**
     * 连接到客户端
     */
    public void Connect() throws IOException {
        zooKeeper = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
            @Override
            public void process(WatchedEvent watchedEvent) {

            }
        });
    }

    /**
     *注册服务器
     */
    public void register(String host) throws KeeperException, InterruptedException {
        String s = zooKeeper.create(Zenum.parNode+"/server", host.getBytes(),
                ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
        System.out.println("s = " + s);
    }

    /**
     * 业务
     */
    public void  business(String host) throws InterruptedException {
        System.out.println(host);
        Thread.sleep(50000000);
    }
}
//客户端


public class Client {

    private ZooKeeper zooKeeper;
    
    public static final String connectString = "主机名1:2181,主机名2:2181,主机名3:2181";

	public static final Integer sessionTimeout = 2000;

	public static final String parNode = "/servers";

    public static void main(String[] args) throws IOException, KeeperException, InterruptedException {
        //创建连接
        Client client = new Client();
        client.Connect();
        //获取服务列表
        client.getServers();
        //业务
        client.business();
    }

    /**
     * 连接到客户端
     */
    public void Connect() throws IOException {
        zooKeeper = new ZooKeeper(Zenum.connectString, Zenum.sessionTimeout, new Watcher() {
            @Override
            public void process(WatchedEvent watchedEvent) {
                try {
                    getServers();
                }catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    /**
     * 获取服务列表
     */
    public void getServers() throws KeeperException, InterruptedException {
        //服务子节点信息
        List<String> childrens = zooKeeper.getChildren(Zenum.parNode, true);
        List<String> list = new ArrayList<>();
        childrens.forEach((item)->{
            try {
                byte[] data = zooKeeper.getData(Zenum.parNode + "/" + item, false, null);
                list.add(data.toString());
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
        System.out.println("======================================");
        System.out.println(list);
    }

    /**
     * 业务
     */
    public void  business() throws InterruptedException {
        Thread.sleep(50000000);
    }
}

zookeeper原理

节点类型

持久节点:

​ 客户端和服务器之间断开连接,节点依旧存在

短暂节点:

​ 客户端和服务器之间断开连接,节点就会删除

持久节顺序编号节点:

​ 客户端和服务器之间断开连接,节点依旧存在,zookeeper给该节点进行顺序编号

短暂节顺序编号节点:

​ 客户端和服务器之间断开连接,节点就会删除,zookeeper给该节点进行顺序编号

监听器原理

1、在main线程中创建zookeeper客户端,

2、客户端会创建两个线程,connect(负责网络通信),Listener(负责监听)

3、通过connect线程将监听事件发送给zookeeper

4、zookeeper将监听到的事件添加到列表中

5、当数据发生变化时

6、Listener会调用process方法
在这里插入图片描述

选举机制

集群中只有要一半以上的节点存活,zookeeper集群就能正常服务,所有zookeeper适合安装奇数台服务器

zookeeper工作时,只要一个leader,其他的为follower,leader是通过选举机制临时产生的。

选举leader采用投票机制,得票数超过一半以上的节点数量的服务器为leader

写入流程

1、当客户端向服务器请求写入

2、判断该服务器是否为leader,如果不是leader将请求进一步转发给leader

3、leader将写入的请求广播给各个服务器,各个服务器将该写入请求加入待写队列,向leader发送成功信息

4、当leader收到半数以上的成功信息,会向各个服务器发送提交信息,各个服务器落实队列写请求,写成功了

5、服务器会通知客户端写入数据成功

ss方法

[外链图片转存中…(img-AgOIzRep-1622556086032)]

选举机制

集群中只有要一半以上的节点存活,zookeeper集群就能正常服务,所有zookeeper适合安装奇数台服务器

zookeeper工作时,只要一个leader,其他的为follower,leader是通过选举机制临时产生的。

选举leader采用投票机制,得票数超过一半以上的节点数量的服务器为leader

写入流程

1、当客户端向服务器请求写入

2、判断该服务器是否为leader,如果不是leader将请求进一步转发给leader

3、leader将写入的请求广播给各个服务器,各个服务器将该写入请求加入待写队列,向leader发送成功信息

4、当leader收到半数以上的成功信息,会向各个服务器发送提交信息,各个服务器落实队列写请求,写成功了

5、服务器会通知客户端写入数据成功

在这里插入图片描述
微信公众号:逸舟学Java

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐