zookeeper

zookeeper快速上手

zookeeper的基本功能和应用场景

 

zookeeper的整体运行机制

 

zookeeper的数据存储机制

数据存储形式

zookeeper中存储数据的基本形式为: key , value

只是zk有点特别:

key:是以路径的形式表示的,那就意味着,各key之间有父子关系,比如

/ 是顶层key

用户建的key只能在/ 下作为子节点,比如建一个key: /aa  这个key可以带value数据

也可以建一个key:   /bb

也可以建key: /aa/xx

zookeeper中,每一个key-value称为一个znode(zookeeper数据节点)

综上所述,zk中的数据存储形式如下:

 

znode类型

zookeeper中的数据节点有4种类型:

  1. 持久节点:客户端一旦建立,zk会持久保存,除非有客户端手动删除
  2. 短暂节点:创建这个节点的客户端一旦断开与zookeeper集群的联系,zookeeper集群就会自动将该节点删除
  3. 带序号的节点:在同一个父节点下,建带序号的子节点,zk会自动给客户端指定的子节点名后拼接一个自增的序号
  4. 不带序号的节点:

上述4中类型,可以有以下组合类型:

持久-带序号

持久-不带序号

短暂-带序号

短暂-不带序号

 

 

zookeeper的集群部署

  1. 上传安装包到集群服务器
  2. 解压
  3. 修改配置文件

进入zookeeper的安装目录的conf目录

cp zoo_sample.cfg zoo.cfg

vi zoo.cfg

# The number of milliseconds of each tick

dataDir=/home/ws/apps/zookeeper/zkdata


#autopurge.purgeInterval=1

server.1=node1:2888:3888

server.2=node2:2888:3888

server.3=node3:2888:3888

对3台节点,都创建目录 mkdir /home/ws/apps/zookeeper/zkdata

对3台节点,在工作目录中生成myid文件,但内容要分别为各自的id: 1,2,3

node1上:  echo 1 > /home/ws/apps/zookeeper/zkdata/myid

node2上:  echo 2 > /home/ws/apps/zookeeper/zkdata/myid

node3上:  echo 3 >/home/ws/apps/zookeeper/zkdata/myid

 

从node1上scp安装目录到其他两个节点

scp -r zookeeper-3.4.6/ node1:$PWD

scp -r zookeeper-3.4.6/ node2:$PWD

 

启动zookeeper集群

zookeeper没有提供自动批量启动脚本,需要手动一台一台地起zookeeper进程

在每一台节点上,运行命令:

bin/zkServer.sh start

启动后,用jps应该能看到一个进程:QuorumPeerMain

 

但是,光有进程不代表zk已经正常服务,需要用命令检查状态:

bin/zkServer.sh status

能看到角色模式:为leader或follower,即正常了。

补充:编写简单批量启动脚本

第一步:vim  zkmanage.sh

第二步:编写脚本,代码如下:

#!/bin/bash
for host in node1 node2 node3
do
echo "${host}:${1}ing......."
ssh $host "source /etc/profile;/home/ws/apps/zookeeper/zookeeper-3.4.6/bin/zkServer.sh $1"
done

 第三步:为了方便,将zkmanage.sh更改为可执行文件

chmod +x zkmanage.sh

第四步:执行

# 启动所有节点上zk
./zkmanage.sh start

# 关闭所有节点上zk
./zkmanage.sh stop

# 查看所有节点上的状态
./zkmanage.sh status

 

 

zookeeper的命令行客户端操作

 

数据管理功能:

##创建节点
create /aaa 'ppppp'

##查看节点下的子节点   
ls /aaa

##获取节点的v
alue: get /aaa

##修改节点的value 
set /aaa 'mmmmm'

##删除节点
rmr /aaa

 

数据监听功能:

## 查看/aaa的子节点的同时,注册了一个监听“节点的子节点变化事件”的监听器
ls /aaa watch  


## 获取/aaa的value的同时,注册了一个监听“节点value变化事件”的监听器
get /aaa watch

注意:注册的监听器在正常收到一次所监听的事件后,就失效

 

 

zookeeper简单demo-分布式系统服务器上下线感知

demo背景假设

 假设现在我在node1上有一个服务,提供时间查询。此时,由于node1提供服务时压力太大,于是我在node2上也布置了同样的服务,并打算将node2也面向消费者提供服务,但是目前消费者只知道node1上有服务,并不知道node2上也提供同样的服务。更坏的情况是如果node1 死掉了(dead),消费者就得不到服务,怎么让消费者知道node2也可以提供服务呢?如果将来还要加入node3、node4.....,消费者怎么获取信息。

demo示意图

 代码实现

TimeQueryServer.java
public class TimeQueryServer {
	ZooKeeper zk = null;
	
	// 构造zk客户端连接
	public void connectZK() throws Exception{
		
		zk = new ZooKeeper("hdp-01:2181,hdp-02:2181,hdp-03:2181", 2000, null);
		
		
	}

	// 注册服务器信息
	public void registerServerInfo(String hostname,String port) throws Exception{
		
		/**
		 * 先判断注册节点的父节点是否存在,如果不存在,则创建
		 */
		Stat stat = zk.exists("/servers", false);
		if(stat==null){
			zk.create("/servers", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
		}
		
		// 注册服务器数据到zk的约定注册节点下
		String create = zk.create("/servers/server", (hostname+":"+port).getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
		
		System.out.println(hostname+" 服务器向zk注册信息成功,注册的节点为:" + create);
		
	}
	
	
	
	public static void main(String[] args) throws Exception {
		
		TimeQueryServer timeQueryServer = new TimeQueryServer();
		
		// 构造zk客户端连接
		timeQueryServer.connectZK();
		
		// 注册服务器信息
		timeQueryServer.registerServerInfo(args[0], args[1]);
		
		// 启动业务线程开始处理业务
		new TimeQueryService(Integer.parseInt(args[1])).start();
		
	}

}

 

TimeQueryService.java
public class TimeQueryService extends Thread{
	
	int port = 0;
	public TimeQueryService(int port){
		
		this.port = port;
	}

	@Override
	public void run() {
		
		try {
			ServerSocket ss = new ServerSocket(port);
			System.out.println("业务线程已绑定端口"+port+"准备接受消费端请求了.....");
			while(true){
				Socket sc = ss.accept();
				InputStream inputStream = sc.getInputStream();
				OutputStream outputStream = sc.getOutputStream();
				outputStream.write(new Date().toString().getBytes());
			}
			
		} catch (IOException e) {
			e.printStackTrace();
		}
		
	}

}

Consumer.java

 

public class Consumer {

	// 定义一个list用于存放最新的在线服务器列表
	private volatile ArrayList<String> onlineServers = new ArrayList<>();
	// 构造zk连接对象
	ZooKeeper zk = null;
	// 构造zk客户端连接
	public void connectZK() throws Exception {

		zk = new ZooKeeper("hdp-01:2181,hdp-02:2181,hdp-03:2181", 2000, new Watcher() {
			@Override
			public void process(WatchedEvent event) {
				if (event.getState() == KeeperState.SyncConnected && event.getType() == EventType.NodeChildrenChanged) {

					try {
						// 事件回调逻辑中,再次查询zk上的在线服务器节点即可,查询逻辑中又再次注册了子节点变化事件监听
						getOnlineServers();
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			}
		});

	}

	// 查询在线服务器列表
	public void getOnlineServers() throws Exception {

		List<String> children = zk.getChildren("/servers", true);
		ArrayList<String> servers = new ArrayList<>();

		for (String child : children) {
			byte[] data = zk.getData("/servers/" + child, false, null);

			String serverInfo = new String(data);

			servers.add(serverInfo);
		}

		onlineServers = servers;
		System.out.println("查询了一次zk,当前在线的服务器有:" + servers);

	}

	public void sendRequest() throws Exception {
		Random random = new Random();
		while (true) {
			try {
				// 挑选一台当前在线的服务器
				int nextInt = random.nextInt(onlineServers.size());
				String server = onlineServers.get(nextInt);
				String hostname = server.split(":")[0];
				int port = Integer.parseInt(server.split(":")[1]);

				System.out.println("本次请求挑选的服务器为:" + server);

				Socket socket = new Socket(hostname, port);
				OutputStream out = socket.getOutputStream();
				InputStream in = socket.getInputStream();

				out.write("haha".getBytes());
				out.flush();

				byte[] buf = new byte[256];
				int read = in.read(buf);
				System.out.println("服务器响应的时间为:" + new String(buf, 0, read));

				out.close();
				in.close();
				socket.close();

				Thread.sleep(2000);
			} catch (Exception e) {
				e.printStackTrace();
			}

		}

	}

	public static void main(String[] args) throws Exception {
		Consumer consumer = new Consumer();
		// 构造zk连接对象
		consumer.connectZK();
		// 查询在线服务器列表
		consumer.getOnlineServers();
		// 处理业务(向一台服务器发送时间查询请求)
		consumer.sendRequest();

	}

}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值