ZooKeeper笔记04-ZooKeeper实战

分布式安装部署

在hadoop102,hadoop103,hadoop104上分别安装ZooKeeper,参考本地模式安装,配置好一台后,其他2台通过xsync.sh命令进行同步,当然也可以安装3遍。

# 同步apache-zookeeper-3.6.3-bin/目录到hadoop102,hadoop103,hadoop104
[root@hadoop102 module]# xsync.sh apache-zookeeper-3.6.3-bin/

配置服务器编号

# 进入hadoop102的data目录下,创建myid文件,并填充值2
[root@hadoop102 module]# cd apache-zookeeper-3.6.3-bin/data/
[root@hadoop102 data]# echo 2 >> myid
# 同理,在hadoop103,hadoop104上也创建myid,分别填充3和4
[root@hadoop102 data]# ssh hadoop103
Last login: Sun Jun 27 11:07:50 2021 from 192.168.216.1
[root@hadoop103 ~]# cd /opt/module/apache-zookeeper-3.6.3-bin/data/
[root@hadoop103 data]# echo 3 >> myid
[root@hadoop103 data]# ssh hadoop104
Last login: Sun Jun 27 11:07:53 2021 from 192.168.216.1
[root@hadoop104 ~]# cd /opt/module/apache-zookeeper-3.6.3-bin/data/
[root@hadoop104 data]# echo 4 >> myid
[root@hadoop104 data]# exit
登出
Connection to hadoop104 closed.
[root@hadoop103 data]# exit
登出
Connection to hadoop103 closed.

配置zoo.cfg配置文件,集群模式,需要添加集群服务器的信息
参数解释:server.A=B:C:D。
其中,A是ZooKeeper里的myid,B是服务器的IP地址,C是Follower与Leader进行数据通信的端口,D是集群中用于选举通信的端口。

# 在hadoop102,hadoop103,hadoop104上添加如下内容
#######################cluster##########################
server.2=hadoop102:2888:3888
server.3=hadoop103:2888:3888
server.4=hadoop104:2888:3888

客户端命令行操作

因为这里使用的是ZooKeeper3.6.3版本,和视频里的版本不一样,命令也略有不同,官方文档在这里

# 查看zkCli.sh支持的所有命令
[zk: localhost:2181(CONNECTED) 0] help
# 关闭zkCli.sh
[zk: localhost:2181(CONNECTED) 1] close
# 连接到hadoop103机器的ZooKeeper
[zk: localhost:2181(CONNECTED) 2] connect hadoop103:2181
# 创建一个持久结点,这个版本,可以不指定结点的值
[zk: localhost:2181(CONNECTED) 3] create /persistent_node
# 创建一个短暂结点,这个版本,可以不指定结点的值
[zk: localhost:2181(CONNECTED) 4] create -e /ephemeral_node
# 创建一个持久序列结点,这个版本,可以不指定结点的值
[zk: localhost:2181(CONNECTED) 5] create -s /persistent_sequential_node
# 创建一个短暂序列结点,这个版本,可以不指定结点的值
[zk: localhost:2181(CONNECTED) 6] create -s -e /ephemeral_sequential_node
# 删除结点
[zk: localhost:2181(CONNECTED) 7] delete /key
# 获取节点的值
[zk: localhost:2181(CONNECTED) 8] get /persistent_node
# 获取结点数
[zk: localhost:2181(CONNECTED) 9] getAllChildrenNumber /
# 查看子结点
[zk: localhost:2181(CONNECTED) 10] ls /
# 查看详细信息
[zk: localhost:2181(CONNECTED) 11] ls -s /persistent_node
# 递归查看子结点信息
[zk: localhost:2181(CONNECTED) 12] ls -R /
# 监听结点
[zk: localhost:2181(CONNECTED) 13] ls -w /
# 更新结点的值
[zk: localhost:2181(CONNECTED) 14] set /abc def
# 查看结点状态信息
[zk: localhost:2181(CONNECTED) 15] stat /
# 同步leader和follower之间数据信息
[zk: localhost:2181(CONNECTED) 16] sync /
# 查看ZooKeeper Client的版本信息
[zk: localhost:2181(CONNECTED) 17] version

API应用

新建一个Maven项目,添加pom.xml,这里ZooKeeper版本要和上面的保持一致。

<?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>org.example</groupId>
    <artifactId>demo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <dependencies>
        <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>3.6.3</version>
        </dependency>
    </dependencies>
</project>

在src/main/resource下创建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客户端连接

创建一个测试类。

package com.demo.zookeeper;

import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.junit.Test;

import java.io.IOException;

public class TestZooKeeper {
    // 这里一定是英文逗号分隔,在ConnectStringParser.ConnectStringParser()方法中
    // 可以看到List<String> hostsList = StringUtils.split(connectString, ",");
    private String connectionString = "hadoop102:2181,hadoop103:2181,hadoop104:2181";
    private int sessionTimeout = 2000;
    private ZooKeeper zkClient;

    @Test
    public void init() throws IOException {
        zkClient = new ZooKeeper(connectionString, sessionTimeout, new Watcher() {
            public void process(WatchedEvent watchedEvent) {
            }
        });
    }
}

看到这句话,说明连接成功了。

2021-06-28 07:20:55,594 INFO [org.apache.zookeeper.ZooKeeper]- Initiating client connection, connectString=hadoop102:2181,hadoop103:2181,hadoop104:2181 sessionTimeout=2000 watcher=com.demo.zookeeper.TestZooKeeper$1@2d6d8735

创建子结点

首先将init()方法的@Test改成@Before,添加createNode()测试方法。

@Test
public void createNode() throws KeeperException, InterruptedException {
	// path:结点的key
	// data:结点的value
	// acl:权限控制相关
	// createMode:创建的结点类型
    String result = zkClient.create("/wangshaoyang", "王劭阳".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
    System.out.println(result);
}

获取子结点并监听结点变化

@Test
public void getChildren() throws KeeperException, InterruptedException {
    List<String> children = zkClient.getChildren("/", true);
    for (String child : children) {
        System.out.println(child);
    }
    // 添加sleep是为了监听子节点变化的,如果线程停止了,就监听不到了
    // 同时,还要修改init()方法,当watcher有变化的时候,执行一个回调方法,也就是再调用一遍getChildren()
    Thread.sleep(Long.MAX_VALUE);
}

@Before
public void init() throws IOException {
	// watcher:当监听事件获取到内容的时候,进行回调的方法
    zkClient = new ZooKeeper(connectionString, sessionTimeout, watchedEvent -> {
        List<String> children = new ArrayList<>();
        try {
            children = zkClient.getChildren("/", true);
        } catch (KeeperException | InterruptedException e) {
            e.printStackTrace();
        }
        for (String child : children) {
            System.out.println(child);
        }
    });
}

判断ZNode是否存在

@Test
public void exist() throws KeeperException, InterruptedException {
    Stat exists = zkClient.exists("/wangshaoyang", false);
    System.out.println(exists == null ? "not exist" : "exist");
}

案例实战

在一个分布式系统上,主节点有多台,可以动态上下线,任意一台客户端都想实时感知到主节点的上下线操作。
我们的集群有3台Server和若干台Client,我们将3台Server注册到ZooKeeper集群中,以结点的形式存在,Server的上下线,Client通过getChildren()来判断。
首先在集群上创建servers结点。

[zk: localhost:2181(CONNECTED) 7] create /servers

Server端

package com.demo.zookeeper;

import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.ZooKeeper;

import java.io.IOException;

public class DistributeServer {
    private static String connectString = "hadoop102:2181,hadoop103:2181,hadoop104:2181";
    private static int sessionTimeout = 2000;
    private ZooKeeper zk = null;
    private String parentNode = "/servers";

    // 创建到ZooKeeper客户端连接
    public void getConnect() throws IOException {
        zk = new ZooKeeper(connectString, sessionTimeout, event -> {
        });
    }

    // 注册服务器
    public void registServer(String hostname) throws Exception {
        // 当服务端下线后,结点就会消失,这时候,我们才能观察到效果,所以这里创建EPHEMERAL类型的结点,为了避免path重复,所以创建SEQUENTIAL类型结点
        String create = zk.create(parentNode + "/server", hostname.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
        System.out.println(hostname + " is online " + create);
    }

    // 业务功能
    public void business(String hostname) throws Exception {
        System.out.println(hostname + " is working ...");
        Thread.sleep(Long.MAX_VALUE);
    }

    public static void main(String[] args) throws Exception {
        // 获取ZooKeeper连接
        DistributeServer server = new DistributeServer();
        server.getConnect();
        // 利用ZooKeeper连接注册服务器信息
        server.registServer(args[0]);
        // 启动业务功能
        server.business(args[0]);
    }
}

IDEA设置并行运行,通过Program Arguments传参,启动3次,参数分别为hadoop102,hadoop103,hadoop104。
在这里插入图片描述
在服务端打开一个zkClient,可以看到/servers下面有3个结点了。

[zk: localhost:2181(CONNECTED) 0] ls /servers
[server0000000001, server0000000002, server0000000003]

Client端

package com.demo.zookeeper;

import org.apache.zookeeper.ZooKeeper;

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

public class DistributeClient {
    private static String connectString = "hadoop102:2181,hadoop103:2181,hadoop104:2181";
    private static int sessionTimeout = 2000;
    private ZooKeeper zk = null;
    private String parentNode = "/servers";

    // 创建到 zk 的客户端连接
    public void getConnect() throws IOException {
        zk = new ZooKeeper(connectString, sessionTimeout, event -> {
            // 再次启动监听
            try {
                getServerList();
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    }

    // 获取服务器列表信息
    public void getServerList() throws Exception {
        // 1 获取服务器子节点信息,并且对父节点进行监听
        List<String> children = zk.getChildren(parentNode, true);
        // 2 存储服务器信息列表
        ArrayList<String> servers = new ArrayList<>();
        // 3 遍历所有节点,获取节点中的主机名称信息
        for (String child : children) {
            byte[] data = zk.getData(parentNode + "/" + child, false, null);
            servers.add(new String(data));
        }
        // 4 打印服务器列表信息
        System.out.println(servers);
    }

    // 业务功能
    public void business() throws Exception {
        System.out.println("client is working ...");
        Thread.sleep(Long.MAX_VALUE);
    }

    public static void main(String[] args) throws Exception {
        // 获取ZooKeeper连接
        DistributeClient client = new DistributeClient();
        client.getConnect();
        // 获取/servers的子节点信息,从中获取服务器信息列表
        client.getServerList();
        // 业务进程启动
        client.business();
    }
}

此时3台Server都在运行,再启动一个Client,此时会自动打印3台Server的名称,依次关掉hadoop102,hadoop103,hadoop104,在Client的Console里,可以看到结点数在依次减少,直到最后都没有了,再把Server依次启动起来,也可以看到Client的Console里依次打印了上线的Server,说明Client可以实时观测到Server端的动态上下线。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值