zookeeper

zookeeper安装包资源

百度网盘链接:点击打开链接 密码:po8x

单机模式安装

实现步骤:

1.准备虚拟机

然后通过xshell来连接

2.关闭防火墙

3.安装jdk和配置环境变量

4.上传安装zookeeper

目录结构:

bin 命令

lib 运行的jar包类库

conf 配置文件

5.配置zk的配置文件

zoo_sample.cfg进行拷贝和重命名为zoo.cfg,注意这个名字写死


 

6.启动zk服务

切换到bin目录:执行 sh zkServer.sh start

7.启动zk客户端,操作zk服务

执行:sh zkCli.sh

Zookeeper命令:

1.znode表示的每个节点。Zookeeper有多个znode节点组成了一个znode

2.znode可以有子节点,此外,znode也可以存储数据

3.znode是有监听机制的,即zonde节点的状态变化是可以被监听的,这个是zookeeper核心特性,很多Zookeeper实现的功能都是基于这个特性来实现的

4.znode节点是可以被创建的,一共有4种类型的节点,分别是:

.持久节点

.临时节点:当创建此临时的客户端退出时,临时节点会消失

.持久顺序节点:在节点名称后,zk会自动在节点后加一个递增的顺序号

.临时顺序节点:

5.znode路径的唯一性

 

create  /park01 数据

需要注意的是:

要基于根路径来创建

②在创建节点的同时,要分配初始数据

③也可以通过 create /park02 ''  相当于创建空数据

④路径的唯一性

 

create -e /park03 数据

创建临时节点

 

create -s /park04 数据

创建持久顺序节点

create -s -e /park05 数据

创建临时顺序节点

 

create /park01/ch1 数数据

创建 pak01的子节点ch1 ,并且这个节点类型是持久节点

 

ls /park01

 

get  /park01 

查看指定路径下的数据

 

delete  /park02

删除节点指令

如果有子节点的话,不能用这个指令

 

rmr /park01

无论如何都会删除节点

 

set /park01 要更新的数据

zookeeperAPI操作

一、

连接zookeeper和创建节点数据代码:

public class ZkDemo {
	
	public static void main(String[] args) throws Exception {
		final CountDownLatch cdl =new CountDownLatch(1);
		//第一个参数,连接的zookeeper节点ip,
		//第二个参数,会话超时时间,以毫秒为单位。比如设置1000*30 是30秒。如果30秒内zookeeper没有收到客户端节点的心跳,则断开连接
		//第三个参数,Watcher,观察者,当zookeeper里有事件发生时,会通知到这个process方法里
		//需要注意,连接是异步的,当连接成功是,会回调process方法,我们需要做相应的处理
		//此外,连接一个zookeeper连接地址也可以,因为zookeep会将数据同步至其他的zookeerper节点
		ZooKeeper zk=new ZooKeeper("192.168.234.137:2181",30000,new Watcher(){

			@Override
			public void process(WatchedEvent event) {
				if(event.getState()==KeeperState.SyncConnected){
					//说明连接成功了
					cdl.countDown();
				}	
			}	
		});
		//之所以用递减锁,因为:连接是非阻塞的,到底连接成功没,不知道。所以,用递减锁,把这个连接变成了阻塞式连接,当成功连接后,才走
		//await()之后的代码
		cdl.await();
		//创建节点
		//1.path 节点路径;2.data[] 节点数据;3.acl 权限策略,一般设为所用用户都可以操作这个节点,并且具有所有权限,所以选:Ids.OPEN_ACL_UNSAFE;
		//4.createMode 创建模式:
		//PERSISTENT 持久
		//PERSISTENT_SEQUENTITAL 持久顺序
		//EPHEMERAL 临时
		//EPHEMERAL_SEQUENTITAL 临时顺序
		zk.create("/node", "hello1604Zookeeper".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
		
		//也可以通过StringCallBack()回调函数来拿到创建后的节点名。rc 是返回码。0代表节点创建成功。-110代表此节点已存在,节点创建失败。
		//ctx 可以是任意一个对象,这个对象也可以回调函数里接收
		//需要注意的是,如果是用这种方式来创建节点,是非阻塞的
		zk.create("/02","helloZK".getBytes(),Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT,new StringCallback() {
					
			public void processResult(int rc, String path, Object ctx, String name) {
				System.out.println(rc+":"+path+":"+ctx+":"+name);
				}
			}, 1);	
	}
}

更新节点数据:

@Test
	public void testSet() throws Exception{
		final CountDownLatch cdl=new CountDownLatch(1);
		ZooKeeper zk=new ZooKeeper("192.168.234.137:2181",30000,new Watcher(){

			@Override
			public void process(WatchedEvent event) {
				if(event.getState()==KeeperState.SyncConnected){
					cdl.countDown();
				}
				
			}
			
		});
		cdl.await();
		//version版本号的作用:在指定路径下,有一个dataVersion,这个是数据更改的版本号,从0开始,在此节点下
		//每更改一次数据,版本号递增1
		//而setData(, ,version)的version的意思是:指定一个版本号,基于这个版本号来修改,但是需要注意:
		//如果指定的版本号是3,而当前的dataVersion是4,即指定的版本号不存在(已过时),则会报错
		//如果指定的版本号是-1,则说明无论当前dataVersion是多少,都会进行数据的更新,最常用的就是-1
		zk.setData("/node", "ccc".getBytes(), -1);
	}

三、

获取节点数据

@Test
	public void testGetData() throws Exception{
		final CountDownLatch cdl=new CountDownLatch(1);
		ZooKeeper zk=new ZooKeeper("192.168.234.137:2181",3000,new Watcher(){

			@Override
			public void process(WatchedEvent event) {
				cdl.countDown();
			}
		});
		cdl.await();
		//watch,当指定节点发生监听的事件时,会触发Watcher里的方法,
		//监听的事件有:1.节点数据发生变化 2.节点创建 3.节点删除 4.节点的子节点发生变化。但是监听的事件只会触发一次回调方法。
		//stat,节点环境信息,一般设置为null即可。如果想要,可以传入一个空的Stat对象来接收
		byte[] data=zk.getData("/node",new Watcher(){

			@Override
			public void process(WatchedEvent event) {
				if(event.getType()==EventType.NodeDataChanged){
					System.out.println("节点数据发生变化");
				}
				
			}
			
		},null);
		System.out.println(new String(data));
		//保持线程开启
		
		//getACL,得到指定路径的权限信息,权限信息被封装在返回的集合里
		//此外,也可以传入一个Stat对象,用来分装节点的环境信息,比如zxid,ctime等等
		Stat s1=new Stat();
		List<ACL> list=zk.getACL("/02",s1);
		for(ACL a:list){
			System.out.println(a);
		}
		System.out.println(s1);
		
		while(true){}
	}

获取子节点

@Test
	public void testGetChildren() throws Exception{
		//连接代码省略
		List<String> list=zk.getChildren("/", null);
		for(String s:list)System.out.println(s);
	}

删除节点

@Test
	public void testDelete() throws Exception{
		//连接代码略
		zk.delete("/node", -1);
		
}

判断节点是否存在

@Test
	public void testExists() throws Exception{
		//如果节点不存在,stat返回值=null
		//如果节点存在,stat返回节点的元数据
		Stat stat=zk.exists("/node02", null);
		System.out.println(stat);
		
}

获取创建的节点信息:如果创建的是临时节点该方法很有必要知道

@Test
publicvoid testCreateByAsynchronous() throws Exception{
zk.create("/node","helloworld".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT,
new StringCallback() {
 
public void processResult(int rc, String path, Object ctx,String name) {
System.out.println("节点创建成功"+rc+":"+path+":"+ctx+":"+name);
 
}
},null);
 
}

实现永久监听

第一种方式代码:
public class TestZK {
	@Test
	public void test01() throws Exception{
		final CountDownLatch cdl=new CountDownLatch(1);
		ZooKeeper zk=new ZooKeeper("192.168.234.137:2181",30000,new Watcher(){

			@Override
			public void process(WatchedEvent event) {
				System.out.println("已连接zookeeper服务");
				cdl.countDown();
				
			}
			
		});
		cdl.await();
		Watcher wc=new Watcher(){
			public void process(WatchedEvent event) {
				if(event.getType().equals(EventType.NodeDataChanged)){
					System.out.println("节点数据发生变化");
				}
			};
		};
		for(;;){
			zk.getData("/engine1/engine01", wc,null);
		}
	}
}

第二种方式代码(建议使用):
@Test
	public void test01() throws Exception{
		final CountDownLatch cdl=new CountDownLatch(1);
		ZooKeeper zk=new ZooKeeper("192.168.234.137:2181",30000,new Watcher(){

			@Override
			public void process(WatchedEvent event) {
				System.out.println("已连接zookeeper服务");
				cdl.countDown();
				
			}
			
		});
		cdl.await();
		
		for(;;){
			final CountDownLatch cdll=new CountDownLatch(1);
			zk.getData("/engine1/engine01", new Watcher(){
				@Override
				public void process(WatchedEvent event) {
					if(event.getType().equals(EventType.NodeDataChanged)){
						System.out.println("节点数据发生变化");
						cdll.countDown();
					}
					
				}
			},null);
			cdll.await();
			
		}
	}
永久监听+得知更新后数据:
@Test
	public void testGetData() throws Exception{
		final CountDownLatch cdl=new CountDownLatch(1);
		ZooKeeper zk=new ZooKeeper("192.168.234.170:2181",2000,new Watcher(){

			@Override
			public void process(WatchedEvent event) {
				if(event.getState()==KeeperState.SyncConnected){
					System.out.println("已成功连接服务器");
					cdl.countDown();
					
				}
				
			}
			
		});
		cdl.await();
		
		for(;;){
			final AuthCallBack ac=new AuthCallBack();
			final CountDownLatch cdl2=new CountDownLatch(1);
			zk.getData("/02",new Watcher(){

				@Override
				public void process(WatchedEvent event) {
					if(event.getType()==EventType.NodeDataChanged){
						System.out.println("有节点数据发生变化");
						System.out.println("更新后的数据"+ac.getNewData());
						cdl2.countDown();
					}
					
				}
				
			},ac,null);
			cdl2.await();
		}
		
	}
	
class AuthCallBack implements DataCallback {

	private String newData;

	@Override
	public void processResult(int rc, String path, Object ctx, byte[] data, Stat stat) {
		this.newData = new String(data);

	}

	public String getNewData() {
		return newData;
	}

	public void setNewData(String newData) {
		this.newData = newData;
	}
	
}

zookeeper集群搭建

1.准备三台虚拟机

2.每台虚拟机防火墙关掉

执行:service iptables stop(如果是centos7将防火墙策略情况即可)

3.配置JDK

4.安装Zookeeper

5.对配置文件进行配置

(1)对zoo_sample.cfg进行拷贝和重命名为zoo.cfg,注意这个名字写死

tickTime =2000 毫秒

minSessiontimeout=tickTime*2

maxSessiontimeout=tickTime*20

Zookeeper的规定:如果API指定的SessionTimeout在区间范围内2s~40s,Zookeeper取得就是API里指定的时间。

比如传的是1s   1<2 ,则用的是下限2s

如果41s 41>40 则用的是上限40s

 

initLimit  Follower初始化连接时和Leader连接的超时时间。initLimit=tickTime*10(initLimit的参数)。注意:FollowerLeader针对的是zk服务集群而言的,一个zk服务集群里只有一个Leader

syncLimit:当FollowerLeader连接上之后,LeaderFollower要实时进行数据通信,如果某一FollowersyncLimit时间期限内,没有和Leader建立通信,则Leader将其抛弃

应用:比如在网络环境不好的时候,可以适当调大此值。

synclimit=tickTime*syncLimit


dataDir默认的数据存储路径是:/tmp/zookeeper。但是这个路径是一个临时目录路径。所以我们需要针对这个参数进行更改:/home/software/zookeeper-3.4.7/tmp


server.1=192.168.234.186:2888:3888

server.2=192.168.234.187:2888:3888

server.3=192.168.234.188:2888:3888

 

server是个关键字,后面跟一个数字,这个数字指定就是当前zookeeper的领导id

2888端口是原子广播端口,zk服务集群在进行集群间数据中一致性通信时,是通过这个端口来通信。当然这个端口,不唯一,可以自己指定。

3888是选举端口,zk服务集群的Leader是通过选举机制来选出来的,在选举时的通信端口是3888。当然这个端口可以指定。

 

(2)配置文件配完之后,创建指定的目录  tmp

(3)然后进入tmp目录,创建myid文件,并写当前zookeeper的领导id,就是server.1的那个数字

zookeeper集群中的选举机制原理

由于内容偏长,改为了资源,见我的资源中:zookeeper选举机制图

详细介绍了zookeeper集群中的leader是如何选举出来的

Zk的选举机制,是基于Paxos这个算法来实现,这个算法的核心思想是:

要解决在分布式环境下,就某一个协议达成一致的算法。

zk 用的是fast paxos 算法,相当于做了改进。

Paxos算法容易在分布式环境下产生活锁。

zookeeper集群命令

由于内容偏长,改为了资源,见我的资源中:zookeeperj集群命令

操作zookeeper集群的api

package com.liming.zookeeper;

import java.util.concurrent.CountDownLatch;
import org.apache.zookeeper.AsyncCallback.StringCallback;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.Watcher.Event.EventType;
import org.apache.zookeeper.Watcher.Event.KeeperState;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;
import org.junit.Before;
import org.junit.Test;

public class TestZookeeperCluster {
	static ZooKeeper zk;
	@Before//连接zookeeper集群
	public void initZk() throws Exception {
		final CountDownLatch cdl = new CountDownLatch(1);
		zk = new ZooKeeper("192.168.239.129:2181,192.168.239.130:2181,192.168.239.131:2181", 3000,
				new Watcher() {
					public void process(WatchedEvent event) {
						//KeeperState.SyncConnected代表连接成功事件
						if(event.getState()==KeeperState.SyncConnected){
							System.out.println("连接成功");
							cdl.countDown();
						}
					}
				});
		cdl.await();
	}
	@Test//永久监听zookeeper集群节点的数据变化
	public void testWatch() throws Exception{
		for(;;){
			final CountDownLatch cdl = new CountDownLatch(1);
			zk.getData("/park01", new Watcher() {
				
				public void process(WatchedEvent event) {
					if(event.getType()==EventType.NodeDataChanged){
						System.out.println("节点数据发生变化");
						cdl.countDown();
					}
				}
			}, null);
			cdl.await();
		}
	}
	@Test
	//永久监听zookeeper集群子节点的事件变化
	//park01子节点的删除、创建会被监听到,子节点数据变化,此方法不会监听到还得使用getdata
	public void testWatchChildren() throws Exception{
		for(;;){
			final CountDownLatch cdl = new CountDownLatch(1);
			zk.getChildren("/park01", new Watcher() {
				
				public void process(WatchedEvent event) {
					if(event.getType()==EventType.NodeChildrenChanged){
						System.out.println("节点数据发生变化");
						cdl.countDown();
					}
				}
			});
			cdl.await();
		}
	}
	@Test//永久监听节点的穿件删除事件
	public void testCreate_delete() throws Exception{
		//exists方法如果该路径存在将返回信息封装到stat对象中,反之返回null
		for(;;){
			final CountDownLatch cdl = new CountDownLatch(1);
			Stat exists = zk.exists("/park05", new Watcher() {
				
				public void process(WatchedEvent event) {
					if(event.getType()==EventType.NodeCreated){
						System.out.println("节点被创建");
						cdl.countDown();
					}else if (event.getType()==EventType.NodeDeleted) {
						System.out.println("节点被删除");
						cdl.countDown();
					}
				}
			});
			cdl.await();
		}
	}
	/*
	 * 利用回调接口拿到创建的实际路径节点名
	 * 应用场景:如果我们创建的Xxx顺序节点,这个节点我们需要想办法拿到。
	 */
	@Test
	public void testCreateStringCallBack() throws Exception{
		zk.create("/park06","".getBytes(),Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL_SEQUENTIAL, 
			new StringCallback() {
				//rc:状态码,成功返回0,不成功-110
				//path 用户指定的路径
				//ctx 构造参数里传递的数据
				//name 实际创建的路径节点名
				public void processResult(int rc, String path, Object ctx, String name) {
					
					System.out.println(rc+":"+path+":"+ctx+":"+name);
				}
			},"hello");
		while(true);
	}
}

Zookeeper观察者状态

Leader Follower Observer

比如针对事务决议的表决,通过投票表决。过半同意事务就更新。

如果从性能角度来看,参与投票的机器越少越好。(但至少满足过半投票人数)

所以zookeeper引入了observer观察者的状态。

observer观察者的特点:

1.不参与投票

2.观察和监听投票结果,然后去更新状态。

如何来设置观察者:

实现步骤:

1.在要变成观察者的机器的zk的配置文件

加:peerType=observer

server.3=ip:2888:3888:observer

2.更改其他机器的配置文件,加:

server.3=ip:2888:3888:observer

引入观察者,一是通过减少投票机器数量,从而提高性能。

此外,也可以从降低每一台zk服务器的负载压力角度来看,能够降低负载压力。

因为观察者如果宕机,影响也不大,因为不是核心人员。

但是注意,引入观察者,从性能角度来看,是好东西。但是如果从集群高可用的角度来看,要 慎用。

此外再注意,比如3台机器,把两台配置成观察者,这么做是不行的,因为不满足过半机制, 所以选不出leader,导致集群启动不了。

Zookeeper应用场景

实现数据的订阅和发布

案例:

比如一台机器发布数据,其他的机器都能够收到发布的最新数据

实现思路:

1.创建一个节点,这个节点是数据发布方的节点,存储数据(1mb

2.其他机器通过监听这个节点的数据变化,拿到最新数据

实现配置信息的统一管理

实现思路参考数据的订阅发布

实现集群管理

实现思路:

1.客户端注册一个临时节点(临时顺序节点)

2.当宕机或失去连接后,临时节点消失

3.通过API可以监听到这个事件的变化,从而做后续的业务处理

实现分布式协调效果(屏障效果barriar

案例:比如,成绩排名,排名的前提是前两台机器都把活做完了,才能做统计排名。

这相当于是分布式环境下的线程同步协调

实现思路:

1.可以创建一个/score节点,然后成绩统计节点把活做完后,在/score创建一个子节点

2.做最后统计排名的节点,去监听/socre子节点的状态变化以及统计子节点的数量,如果是2 个,就可以开始干活了。

实现分布式锁的效果(共享锁)

实现思路:

1.创建一个/lock路径

2.三个机器争抢去在/lock路径下去抢注Xxx顺序节点,最先抢注的节点的顺序号肯定是最小的

3.然后通过比较,去决定哪个机器去用这个共享资源

Zookeeper特点

1.数据一致性

无论从哪一台zk服务器读取数据,数据都是一样的。(也叫单一视图)。是zookeeper最重要 的特性。

2.可靠性

当一个事务被成功更新后,不会因为网络波动或其他原因而回滚

3.原子性

一个事务,要么都更新成功,要么都更新失败。不会有中间状态

4.实时性

能够在很短的时间内,将事件的状态变化反映出来。(前提是网络状态良好,没有网络波动)

5.顺序一致性

比如客户端A,发出三个指令:x=a,x=b,x=c。但是在网络波动的情况下,可能到达顺序是 x=b,x=c,x=aZk能够确保发送的顺序和最后执行的顺序是一致的,从而确保最后的结果是 正确的


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值