Welcome to Apache ZooKeeper(二):集群搭建、客户端操作、API实践

在刚学习zookeeper的时候,经常会对zookeeper安装、基本命令操作等基础概念模糊不清,经常忘记。由此,本文主要对集群的搭建过程、zookeeper的常用命令以及API的操作使用进行记录,为一些需要的朋友提供临时的帮助,也为自己今后的回顾做一个备份。

一、Zookeeper分布式集群搭建

集群规划

在hadoop102、hadoop103和hadoop104三个节点上部署Zookeeper。

解压安装

  1. 解压Zookeeper安装包到/opt/module/目录下

    tar -zxvf zookeeper-3.5.7.tar.gz -C /opt/module/
    复制代码
  2. 同步/opt/module/zookeeper-3.5.7目录内容到hadoop103、hadoop104

    xsync zookeeper-3.5.7/
    复制代码

配置服务器编号

  • 在/opt/module/zookeeper-3.5.7/这个目录下创建zkData

  • 在/opt/module/zookeeper-3.5.7/zkData目录下创建一个myid的文件

  • 编辑myid文件,在文件中添加与server对应的编号

  • 拷贝配置好的zookeeper到其他机器上

    xsync myid
    复制代码

    分发脚本代码:

    #!/bin/bash
    #1. 判断参数个数
    if [ $# -lt 1 ]
    then
      echo Not Enough Arguement!
      exit;
    fi
    #2. 遍历集群所有机器
    for host in hadoop102 hadoop103 hadoop105
    do
      echo ====================  $host  ====================
      #3. 遍历所有目录,挨个发送
      for file in $@
      do
        #4 判断文件是否存在
        if [ -e $file ]
        then
          #5. 获取父目录
          pdir=$(cd -P $(dirname $file); pwd)
          #6. 获取当前文件的名称
          fname=$(basename $file)
          ssh $host "mkdir -p $pdir"
          rsync -av $pdir/$fname $host:$pdir
        else
          echo $file does not exists!
        fi
      done
    done
    复制代码

    快速查询脚本:

    #!/bin/bash
    for i in hadoop102 hadoop103 hadoop104
    do
            echo "========    $i    ========"
            ssh $i "jps" | grep -v Jps
    done
    
    加权限chmod +x all
    sudo mv all /bin/ 移动到bin目录
    复制代码

配置zoo.cfg文件

  • 重命名/opt/module/zookeeper-3.5.7/conf这个目录下的zoo_sample.cfg为zoo.cfg

  • 打开zoo.cfg文件

  • 修改数据存储路径配置

    dataDir=/opt/module/zookeeper-3.5.7/zkData
    
    #添加
    server.2=hadoop102:2888:3888
    server.3=hadoop103:2888:3888
    server.4=hadoop104:2888:3888
    复制代码
  • 同步zoo.cfg配置文件

配置参数解析:

server.A=B:C:D。
复制代码

A是一个数字,表示这个是第几号服务器;

集群模式下配置一个文件myid,这个文件在dataDir目录下,这个文件里面有一个数据就是A的值,Zookeeper启动时读取此文件,拿到里面的数据与zoo.cfg里面的配置信息比较从而判断到底是哪个server。

B是这个服务器的地址;

C是这个服务器Follower与集群中的Leader服务器交换信息的端口;

D是万一集群中的Leader服务器挂了,需要一个端口来重新进行选举,选出一个新的Leader,而这个端口就是用来执行选举时服务器相互通信的端口。

集群操作

  • 分别启动Zookeeper

    bin/zkServer.sh start
    复制代码
  • 查看状态

    bin/zkServer.sh status
    复制代码

二、Zookeeper客户端基础命令使用

zookeeper的命令要在 zookeeper 服务上执行操作。

首先执行命令,打开新的session会话,进入终端:

$ bin/zkCli.sh
复制代码

客户端命令行操作

命令基本语法 功能描述
help 显示所有操作命令
ls path 使用 ls 命令来查看当前znode的子节点 -w 监听子节点变化 -s 附加次级信息
ls2 path 查看当前节点详细数据
create 普通创建 -s 含有序列 -e 临时(重启或者超时消失)
get path 获得节点的值 -w 监听节点内容变化 -s 附加次级信息
set 设置节点的具体值
stat 查看节点状态
delete 删除节点
deleteall 递归删除节点

实例:

#启动客户端:
bin/zkCli.sh
#显示所有操作命令
help
#查看当前znode中所包含的内容
ls /
#查看当前节点详细数据
ls2 /
#分别创建2个普通节点
create /A "node1"
create /A/B "node2"
#获得节点的值
get /A  
"node2"
cZxid = 0x100000003
ctime = Wed Aug 29 00:03:23 CST 2018
mZxid = 0x100000003
mtime = Wed Aug 29 00:03:23 CST 2018
pZxid = 0x100000004
cversion = 1
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 7
numChildren = 1
[zk: localhost:2181(CONNECTED) 6]
[zk: localhost:2181(CONNECTED) 6] get /sanguo/shuguo
liubei
cZxid = 0x100000004
ctime = Wed Aug 29 00:04:35 CST 2018
mZxid = 0x100000004
mtime = Wed Aug 29 00:04:35 CST 2018
pZxid = 0x100000004
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 6
numChildren = 0
#创建短暂节点 只可以在当前会话进行查看
create -e /A/C "LSnode"
#创建带序号的节点
#如果原来没有序号节点,序号从0开始依次递增。如果原节点下已有2个节点,则再排序时从2开始,以此类推。
create -s /A/B/C "node3"
-Created /A/B/C0000000000
create -s /A/B/D "node4"
-Created /A/B/C0000000001
#修改节点数据值
set /A "NEWnode"
#节点的值变化监听
get /sanguo watch
#节点的子节点变化监听(路径变化)
ls /sanguo watch
#删除节点
delete /A/B
#递归删除节点
rmr /sanguo/shuguo
#查看节点状态
stat /A
cZxid = 0x100000003
ctime = Wed Aug 29 00:03:23 CST 2018
mZxid = 0x100000011
mtime = Wed Aug 29 00:21:23 CST 2018
pZxid = 0x100000014
cversion = 9
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 4
numChildren = 1
复制代码

三、API的使用

  1. 创建一个Maven工程,添加pom文件:
<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>
		<!-- https://mvnrepository.com/artifact/org.apache.zookeeper/zookeeper -->
		<dependency>
			<groupId>org.apache.zookeeper</groupId>
			<artifactId>zookeeper</artifactId>
			<version>3.5.7</version>
		</dependency>
</dependencies>
复制代码
  1. log4j.properties文件的创建
log4j.rootCategory=ERROR, console
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.target=System.err
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d{yy/MM/dd HH:mm:ss} %p %c{1}: %m%n

# Set the default spark-shell log level to ERROR. When running the spark-shell, the
# log level for this class is used to overwrite the root logger's log level, so that
# the user can have different defaults for the shell and regular Spark apps.
log4j.logger.org.apache.spark.repl.Main=ERROR

# Settings to quiet third party logs that are too verbose
log4j.logger.org.spark_project.jetty=ERROR
log4j.logger.org.spark_project.jetty.util.component.AbstractLifeCycle=ERROR
log4j.logger.org.apache.spark.repl.SparkIMain$exprTyper=ERROR
log4j.logger.org.apache.spark.repl.SparkILoop$SparkILoopInterpreter=ERROR
log4j.logger.org.apache.parquet=ERROR
log4j.logger.parquet=ERROR

# SPARK-9183: Settings to avoid annoying messages when looking up nonexistent UDFs in SparkSQL with Hive support
log4j.logger.org.apache.hadoop.hive.metastore.RetryingHMSHandler=FATAL
log4j.logger.org.apache.hadoop.hive.ql.exec.FunctionRegistry=ERROR
复制代码
  1. 创建ZooKeeper客户端
private static String connectString =
 "hadoop102:2181,hadoop103:2181,hadoop104:2181";
	private static int sessionTimeout = 2000;
	private ZooKeeper zkClient = null;

	@Before
	public void init() throws Exception {

	zkClient = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
			@Override
			public void process(WatchedEvent event) {

				// 收到事件通知后的回调函数(用户的业务逻辑)
				System.out.println(event.getType() + "--" + event.getPath());

				// 再次启动监听
				try {
					zkClient.getChildren("/", true);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}
}
复制代码
  1. 创建子节点
// 创建子节点
@Test
public void create() throws Exception {
	// 参数1:要创建的节点的路径; 参数2:节点数据 ; 参数3:节点权限 ;参数4:节点的类型
	String nodeCreated = zkClient.create("/A", "node1".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
复制代码
  1. 获取子节点并监听节点变化
@Test
public void getChildren() throws Exception {
	List<String> children = zkClient.getChildren("/", true);
	for (String child : children) {
		System.out.println(child);
	}
	// 延时阻塞
	Thread.sleep(Long.MAX_VALUE);
}
复制代码
  1. 判断Znode是否存在
// 判断znode是否存在
@Test
public void exist() throws Exception {
	Stat stat = zkClient.exists("/eclipse", false);
	System.out.println(stat == null ? "not exist" : "exist");
}
复制代码

监听服务器节点动态上下线案例

某分布式系统中,主节点可以有多台,可以动态上下线,任意一台客户端都能实时感知到主节点服务器的上下线。

具体实现:

先在集群上创建/servers节点

create /servers "servers"
复制代码

服务器端向Zookeeper注册代码

package com.test.zkcase;
import java.io.IOException;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.ZooDefs.Ids;

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";
	
	// 创建到zk的客户端连接
	public void getConnect() throws IOException{
		zk = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
			@Override
			public void process(WatchedEvent event) {
			}
		});
	}
	
	// 注册服务器
	public void registServer(String hostname) throws Exception{
		String create = zk.create(parentNode + "/server", hostname.getBytes(), 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 {
		// 1获取zk连接
		DistributeServer server = new DistributeServer();
		server.getConnect();
		
		// 2 利用zk连接注册服务器信息
		server.registServer(args[0]);
		
		// 3 启动业务功能
		server.business(args[0]);
	}
}
复制代码

客户端代码

package com.test.zkcase;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;

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, new Watcher() {
			@Override
			public void process(WatchedEvent 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 {

		// 1获取zk连接
		DistributeClient client = new DistributeClient();
		client.getConnect();

		// 2获取servers的子节点信息,从中获取服务器信息列表
		client.getServerList();

		// 3业务进程启动
		client.business();
	}
}
复制代码

Guess you like

Origin juejin.im/post/7077115820731531272