文章目录
zookeeper
数据存储形式
zookeeper中对用户的数据采用kv形式存储,只是zk有点特别:
key:是以路径的形式表示的,那就以为着,各key之间有父子关系,比如 / 是顶层key,用户建的key只能在/ 下作为子节点,比如建一个key: /aa 这个key可以带value数据,也可以建一个key: /bb,也可以建key: /aa/xx,zookeeper中,对每一个数据key,称作一个znode
znode类型
zookeeper中的znode有多种类型:
1、PERSISTENT 持久的:客户端一旦建立,zk会持久保存,除非有客户端手动删除
2、EPHEMERAL 短暂的:创建这个节点的客户端一旦断开与zookeeper集群的联系,zookeeper集群就会自动将该节点删除
3、SEQUENTIAL 带序号的:在同一个父节点下,建带序号的子节点,zk会自动给客户端指定的子节点名后拼接一个自增的序号
组合类型:
PERSISTENT :持久不带序号
EPHEMERAL :短暂不带序号
PERSISTENT 且 SEQUENTIAL :持久且带序号
EPHEMERAL 且 SEQUENTIAL :短暂且带序号
zookeeper的集群部署
1、上传安装包到集群服务器
2、解压
3、修改配置文件
进入zookeeper的安装目录的conf目录
cp zoo_sample.cfg zoo.cfg
vi zoo.cfg
# The number of milliseconds of each tick
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/root/zkdata ##修改
clientPort=2181
#autopurge.purgeInterval=1
server.1=n1:2888:3888
server.2=n2:2888:3888
server.3=n3:2888:3888
对3台节点,都创建目录 mkdir /root/zkdata
对3台节点,在工作目录中生成myid文件,但内容要分别为各自的id: 1,2,3
n1上: echo 1 > /data/zkdata/myid
n2上: echo 2 > /data/zkdata/myid
n3上: echo 3 > /data/zkdata/myid
4、从hdp20-01上scp安装目录到其他两个节点
scp -r zookeeper-3.4.6/ n2
P
W
D
s
c
p
−
r
z
o
o
k
e
e
p
e
r
−
3.4.6
/
n
3
:
PWD scp -r zookeeper-3.4.6/ n3:
PWDscp−rzookeeper−3.4.6/n3:PWD
5、启动zookeeper集群
zookeeper没有提供自动批量启动脚本,需要手动一台一台地起zookeeper进程
在每一台节点上,运行命令:
bin/zkServer.sh start
启动后,用jps应该能看到一个进程:QuorumPeerMain
但是,光有进程不代表zk已经正常服务,需要用命令检查状态:
bin/zkServer.sh status
能看到角色模式:为leader或follower,即正常了。
6.编写集群启动
zkServer_all.sh
#!/bin/bash
for host in n1 n2 n3
do
echo "${host}:${1}ing..."
ssh $host "source /etc/profile;/appdata/zookeeper/bin/zkServer.sh $1"
done
启动:./zkServer_all.sh start
查看状态:./zkServer_all.sh status
关闭:./zkServer_all.sh stop
zookeeper的命令行客户端操作
开启命令行客户端:bin/zkCli.sh
注意:在安装有zookeeper的节点都能执行该命令。
zookeeper的数据存储形式:
1、zookeeper中存储数据的基本形式为: key , value
2、zookeeper中的key是用路径表示的:
/aa : 88888
/aa/bb : “xxoo”
/aa/cc : “edu360”
/tt: 9898
每一个key-value称为一个znode(zookeeper数据节点)
数据管理功能:
创建节点: create /aaa ‘ppppp’
查看节点下的子节点: ls /aaa
获取节点的value: get /aaa
修改节点的value: set /aaa ‘mmmmm’
删除节点:rmr /aaa
数据监听功能:
ls /aaa watch
查看/aaa的子节点的同时,注册了一个监听“节点的子节点变化事件”的监听器
在n1机器上:创建/aaa并监听
在n2机器上:创建/aaa/bbb,n1出现变化
get /aaa watch
获取/aaa的value的同时,注册了一个监听“节点value变化事件”的监听器
注意:注册的监听器在正常收到一次所监听的事件后,就失效
在n1进行注册
在n2进行更改数据
zookeeper在windows客户端访问
守护线程使用实例
守护线程的特点:主线程运行完毕,守护线程就会自动死亡。
package com.initialize.thread;
public class ThreadDemo {
public static void main(String[] args) throws InterruptedException {
System.out.println("主线程开始执行......");
System.out.println("主线程准备启动一个子线程。。。。。");
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("子线程开始执行......");
while(true){
try{
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("子线程打印......");
}
}
});
//setDaemon(true); 这个子线程就变成了守护线程
thread.setDaemon(true);
thread.start();
System.out.println("主线程启动子线程后的语句......");
Thread.sleep(10000);
}
}
Windows客户端访问zookeeper
package com.initialize.demo;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.ZooKeeper;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.util.List;
/**
* zookeeper在Windows客户端的演示
*/
public class ZookeeperClientDemo {
ZooKeeper zk = null;
@Before
public void init() throws IOException {
//构造一个连接zookeeper的客户端对象
zk = new ZooKeeper("n1:2181,n2:2181,n3:2181", 2000, null);
}
@Test
public void testCreate() throws KeeperException, InterruptedException {
//参数1:要创建的节点路径 参数2:数据 参数3:访问权限 参数4:节点类型
String create = zk.create("/idea", "hello idea".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
System.out.println(create);
zk.close();
}
@Test
public void testUpdate() throws KeeperException, InterruptedException {
//参数1:节点路径 参数2:数据 参数3:所要修改的版本,-1代表任何版本
zk.setData("/idea", "我爱你".getBytes(), -1);
zk.close();
}
@Test
public void testGet() throws Exception{
//参数1:节点路径 参数2:是否要监听 参数3:要获取数据的版本,null代表最新版本
byte[] data = zk.getData("/idea", false, null);
System.out.println(new String(data, "UTF-8"));
zk.close();
}
@Test
public void testListChildren() throws KeeperException, InterruptedException {
//参数1:节点路径 参数2:是否要监听
//注意:返回的结果中只有子节点名字,不带全路径
List<String> children = zk.getChildren("/aaa", false);
for(String child : children){
System.out.println(child);
}
zk.close();
}
}
Windows客户端监控器
package com.initialize.demo;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.junit.Before;
import org.junit.Test;
import java.util.List;
public class ZookeeperWatchDemo {
ZooKeeper zk = null;
@Before
public void init() throws Exception {
//构造一个连接zookeeper的客户端对象
//参数1:zookeeper服务的主机端口号 参数2:最大超时时间 参数3:注册监听
zk = new ZooKeeper("n1:2181,n2:2181,n3:2181", 2000, new Watcher() {
@Override
public void process(WatchedEvent event) {
if(event.getState() == Event.KeeperState.SyncConnected && event.getType()== Event.EventType.NodeDataChanged){
System.out.println(event.getPath());//收到的事件所发生的节点路径
System.out.println(event.getType());//收到的事件类型
System.out.println("赶紧换照片,换浴室里面的洗浴套装。。。。");
try{
zk.getData("/mygirls", true, null);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (KeeperException e) {
e.printStackTrace();
}
}else if(event.getState() == Event.KeeperState.SyncConnected && event.getType() == Event.EventType.NodeChildrenChanged){
System.out.println("子节点变化了。。。。");
}
}
});
}
@Test
public void testGetWatch() throws Exception{
byte[] data = zk.getData("/mygirls", true, null);//监听节点数据变化
List<String> children = zk.getChildren("/mygirls", true);//监听节点的子节点变化事件
System.out.println(new String(data, "UTF-8"));
Thread.sleep(Long.MAX_VALUE);
}
}
zookeeper图形化客户端插件
在Eclipse环境下安装ZooKeeper状态查看相关的插件步骤如下:
Step 1. 在 Eclipse 菜单打开Help -> Install New Software…
Step 2. 添加 url http://www.massedynamic.org/eclipse/updates/
Step 3. 选择插件并安装运行
Step 4. 在 Eclipse 菜单打开Window->Show View->Other…->ZooKeeper 3.2.2。
Step 5. 连接ZK 输入正在运行的ZK server 地址和端口
zookeeper选举机制示意图
zookeeper应用案例–分布式系统服务器上下线感知
服务器端源码:
package com.initialize.distributesystem;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;
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 inputStream = sc.getInputStream();
OutputStream outputStream = sc.getOutputStream();
outputStream.write(new Date().toString().getBytes());
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
package com.initialize.distributesystem;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;
public class TimeQueryServer {
ZooKeeper zk = null;
//构造zk客户端连接
public void connectZK() throws Exception{
zk = new ZooKeeper("n1:2181,n2:2181,n3: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, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
//注册服务器数据到zk的约定注册节点下
String create = zk.create("/servers/server", (hostname+":"+port).getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_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();
}
}
客户端源码:
package com.initialize.distributesystem;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class Consumer {
//定义一个list用于存放最新的在线服务器列表
private volatile ArrayList<String> onlineServers = new ArrayList<>();
//构造zk连接对象
ZooKeeper zk = null;
//构造zk客户端连接
public void connectZK() throws IOException {
zk = new ZooKeeper("n1:2181,n2:2181,n3:2181", 2000, new Watcher() {
@Override
public void process(WatchedEvent event) {
if(event.getState()== Event.KeeperState.SyncConnected &&event.getType()== Event.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();
}
}
将程序打成jar包,放到不同的Liunx机器上面运行,分别启动server服务和consumer客户端。观察运行结果。
应用实例示意图