zookeeper看这一篇就够了

news2024/9/28 3:11:13

第一章 zookeeper简介

第1节 zookeeper的由来

1
2
3
4
1. zookeeper最早起源于雅虎研究院的一个研究小组
2. 在雅虎内部很多大型系统基本都需要依赖一个类似的系统来进行分布式协调,并且这个系统还有单点问题
3. 雅虎的开发人员就试图开发一个通用的无单点问题的分布式协调框架,以便让开发人员将精力集中在处理业务逻辑上,这是最早zookeeper的原型
4. 后来捐献给了apache成为了apache的顶级项目

第2节 zookeeper名字的由来

1
雅虎研究院很多项目都以动物命名,而zookeeper做为各个项目之间的协调,就起了一个动物管理员的名字(zookeeper),简称zk

第3节 zk应用场景

1
2
3
4
5
1. 注册中心(Dubbo框架介绍)
2. 配置中心
3. 分布式锁(本文不做讲解)
...

第二章 zk的下载

1
2
3
这里以3.4.9版本为例,下面是下载地址,下载zookeeper-3.4.9.tar.gz压缩包(在Windows/Linux/Mac下通用)

https://archive.apache.org/dist/zookeeper/zookeeper-3.4.9/

第三章 zk的单点安装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
1. 平台支持
	1.1 Windows 服务端和客户端开发和生产环境都支持
	1.2 Linux   服务端和客户端开发和生产环境都支持
	
2. 软件环境
	2.1 客户端和服务器端都是用Java语言编写,需要本机安装JDK
	2.2 zookeeper3.4.9版本需要JDK版本大于等于1.6
	
3. 单点安装步骤
	3.1 下载zookeeper安装包
	3.2 解压zookeeper
	3.3 在conf文件夹下找到zoo_sample.cfg将其重命名为zoo.cfg
	3.4 修改配置
		# zookeeper服务器心跳检测
		tickTime=2000
		# zookeeper存放数据的目录,zookeepr服务器可以保存数据
		dataDir=/tmp/zookeeper
		# 服务器启动占用的端口号,用于客户端连接使用
		clientPort=2181
4. 启动和停止
	4.1 启动
		- Windows: bin目录下直接双击 zkServer.cmd
		- Linux  : 使用命令行工具,在bin目录下 zkServer.sh start
	4.2 停止
		- Windows: ctrl+c 或者 使用命令杀进程
		- Linux  : 使用命令行工具,在bin目录下 zkServer.sh stop

第四章 zk客户端使用

第1节 客户端连接

1
连接命令: zkCli.sh -server 127.0.0.1:2181

第2节 zk节点概述

2.1 zk节点概念

1
zk可以看成是一个小型的数据存储系统,他的数据存储结构和Unix文件系统类型类似(树形结构).
zk节点图例Unix/Linux文件系统图例
1
2
3
4
1. zk的数据存储在树形结构的节点中(例如根节点/、app1节点、app2节点以及子节点等)
2. 每个节点都可以存储数据,还可以创建新的子节点
3. 根节点默认存在,其他节点可以手动操作(创建和销毁)
4. 可以使用zk命令创建不同类型的节点,不同类型的节点拥有不同的特性(持久化/非持久化/顺序等)

2.2 zk节点类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
zk节点类型分为4种:
1. 持久节点(PERSISTENT) : 
	1.1 zk客户端连接zk服务器端会话(session)结束后数据不会丢失
	1.2 持久节点在其下方可以创建子节点
	
2. 临时节点(EPHEMERAL) :
	2.1 zk客户端连接zk服务器端会话(session)结束后数据会丢失
	2.2 临时节点在其下方不可以创建子节点
	
3. 持久顺序节点(PERSISTENT_SEQUENTIAL) :
	3.1 zk客户端连接zk服务器端会话(session)结束后数据不会丢失
	3.2 持久节点在其下方可以创建子节点
	3.3 持久节点在创建同名节点时不能创建成功,但是持久顺序节点可以
	3.4 创建持久顺序节点时,zk会在名称后自动添加一个序号,序号是一个单调递增的计数器,由父节点维护
	
4. 临时顺序节点(EPHEMERAL_SEQUENTIAL) :
	4.1 zk客户端连接zk服务器端会话(session)结束后数据会丢失
	4.2 临时节点在其下方不可以创建子节点
	4.3 临时节点在创建同名节点时不能创建成功,但是临时节点顺序节点可以
	4.3 创建持久顺序节点时,zk会在名称后自动添加一个序号,序号是一个单调递增的计数器,由父节点维护

2.3 zk节点操作

  • zk常见命令
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
0. help命令 -- 查看当前环境下都可以使用哪些命令

1. ls命令 -- 查看某一个路径下的目录列表    例如: ls /  查看根目录下的目录列表

2. ls2命令 -- 查看某个路径下目录列表(相比于ls命令信息更详细)  例如: ls2 /   查看根目录下的目录列表

3. create命令 -- 创建节点并赋值  例如: create /ukoko val1   创建了一个ukoko节点,并在此节点存储了val1字符串
	3.1 create的格式为  create [-s] [-e] path data acl
	3.2 [-s] [-e] 中括号的参数可有可无 -s表示创建顺序节点  -e表示创建临时节点 不加默认创建持久节点
	3.3 path 指定要创建节点的路径
	3.4 data 要在此节点存储的数据
	3.5 acl 访问权限相关,默认是 world,全世界都能访问
	
4. get命令 -- 获取节点数据和状态信息 例如: get /ukoko   查看当前节点下的数据以及节点固有属性信息
	4.1 get格式  get path [watch]
	4.2 path 要查询的节点路径
	4.3 [watch] 中括号的参数可有可无, watch对节点进行事件监听  例如: get /ukoko watch   查看这个节点,并且监听这个节点数据变化,测试这个节点时需要开启至少两个客户端
	
5. set命令 -- 修改节点存储的数据  例如: set  /ukoko 100 给ukoko节点设置数据
	5.1 set格式 set path data [version]
	5.2 [version] 中括号的参数可有可无  version版本号
	5.3 path 要设置数据的节点路径
	5.4 data 需要存储的数据

6. delete命令 -- 删除某节点     例如: delete /ukoko
	6.1 delete格式 delete path [version]
	6.2 path 要删除的节点路径
	6.3 [version] 中括号的参数可有可无  version版本号(同set命令)

7. stat命令 -- 查看节点状态信息   例如: stat /ukoko
	7.1 stat格式 stat path [watch]
	7.2 [watch]中括号的参数可有可无

8. rmr 命令 -- 强制删除节点以及下子节点  例如 rmr /app
  • zk节点属性详情
序号属性数据类型描述
1cZxidlong该节点被创建时的事物id
2ctimelong该节点被创建时的创建时间
3mZxidlong该节点最后被修改时的事务id
4mtimelong该节点最后修改时间
5pZxidlong该节点子节点列表最后一次修改的事物id(如果修改子节点里面的数据内容那么事物id不会变化,添加或者删除子节点时此id会变化)
6cversionlong子节点版本号(子节点每次修改数据版本号+1)
7dataVersionlong数据版本号(数据每次修改该版本号+1)
8aclVersionlong权限版本号(权限每次修改该版本号+1)
9ephemeralOwnerlong创建该临时节点的会话的sessionId,如果不是临时节点那么为0
10dataLengthint该节点的数据长度
11numChildrenint该节点子节点的数量
  • zk常见命令使用
1
注意: 所有命令设计到的路径操作必须是绝对路径,不可以是相对路径
    • 创建节点
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
1. 创建持久化节点:
	1.1 命令 create /ukoko val0  创建ukoko节点,并且存储数据val0
	1.2 命令 create /ukoko val1  创建ukoko节点,并且存储数据val1,失败报 Node already exists: /ukoko
	
2. 创建顺序持久化节点:
	2.1 命令 create -s /ukoko  val1 创建ukoko节点,并且存储数据val1
	2.2 命令 create -s /ukoko  val2 创建ukoko节点,并且存储数据val2,成功,因为顺序节点会在节点名称后自动添加序号,使用 ls命令查看
	
3. 创建临时节点
	3.1 命令 create -e /ukoko1  val0 创建ukoko1临时节点,并且存储数据val0
	3.2 使用quit退出命令退出当前会话,会在控制台打印出当前会话id和ephemeralOwner属性对比
	3.3 重新使用客户端命令连接zk服务器,使用ls命令查看,临时节点消失
	
4. 创建顺序临时节点
	4.1 命令 create -s -e /ukoko2 val0 创建ukoko2顺序临时节点,并且存储数据val0
	4.2 命令 create -s -e /ukoko2 val1 创建ukoko2顺序临时节点,并且存储数据val1,成功,因为顺序节点会在节点名称后自动添加序号,使用 ls命令查看
	4.3 使用quit退出命令退出当前会话,会在控制台打印出当前会话id和ephemeralOwner属性对比
	4.3 重新使用客户端命令连接zk服务器,使用ls命令查看,临时节点消失
    • 给节点设置数据
1
2
3
4
5
6
1. 设置普通数据(如果set设置值时不加版本,那么数据版本会随着每次set递增,可以在每次set成功之后查看dataVersion属性)
	1.1 命令 set /ukoko 100
	1.2 查看数据 get /ukoko
2. 设置带版本数据(如果带版本set,那么必须保证版本号与dataVersion版本相同,保证修改的是当前版本数据)
	2.1 命令 set /ukoko 100 1   如果设置数据的版本,与dataVersion不相同会报version No is not valid(版本号无效)
	2.2 查看数据 get /ukoko
    • 获取节点
1
2
3
4
1. 获取普通数据: get /ukoko
2. 获取数据并且添加监听: get /ukoko watch

查看节点数据,并使用watch监听之后,如果对数据节点进行set操作,那么监听器会自动回调  NodeDataChanged path:/ukoko
    • 查看节点状态
1
2
3
4
1. 查看节点状态: stat /ukoko
2. 查看节点状态并对节点进行监听: stat /ukoko watch

查看节点状态,并使用watch监听之后,如果对数据节点进行set操作,那么监听器会自动回调  NodeDataChanged path:/ukoko
    • 删除节点
1
2
3
4
1. 删除节点
	1.1 命令 delete /ukoko 删除节点
2. 删除带版本的节点
	2.1 命令 delete /ukoko 1 如果后面带有版本那么要和dataVersion版本号相同

第3节 使用Java客户端操作znode

1
2
3
zk的Java客户端常见的有两种:
1. 官网提供的zk客户端           功能简单,使用不便,比较底层
2. Apache提供的curator客户端    功能丰富,使用方便

3.1 zk客户端依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/*  zk官网提供  */
<dependency>
    <groupId>org.apache.zookeeper</groupId>
    <artifactId>zookeeper</artifactId>
    <version>3.4.9</version>
</dependency>

/* 
	apache提供
	curator官网: https://curator.apache.org/
	如果zookeeper服务版本使用3.4.x 对应的curator版本4.2.x
*/

<dependency>
	<groupId>org.apache.curator</groupId>
	<artifactId>curator-recipes</artifactId>
	<version>4.2.0</version>
	<exclusions>
		<exclusion>
			<groupId>org.apache.zookeeper</groupId>
			<artifactId>zookeeper</artifactId>
		</exclusion>
	</exclusions>
</dependency>
<dependency>
	<groupId>org.apache.zookeeper</groupId>
	<artifactId>zookeeper</artifactId>
	<version>3.4.9</version>
</dependency>

3.2 节点数据操作

3.2.1 zookeeper操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
@Test
public void test01() throws IOException, KeeperException, InterruptedException {
	//服务器地址: 格式:ip:port ,如果是集群 ip1:port1,ip2:port2,ip3:port3
	String connectString="127.0.0.1:2181";
	//会话超时时间
	int sessionTimeout=3000;

	//创建zk连接
	ZooKeeper zk = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
		/**
		 * Watcher: 监控所有被触发的事件
		 * 触发执行process方法
		 */
		@Override
		public void process(WatchedEvent event) {
			System.out.println("事件触发: "+event);
		}
	});
	//创建一个持久化节点
	//zk.create("/app1","val".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
	//创建一个持久化子节点在app1节点下
	//zk.create("/app1/app1_1","val1".getBytes(),ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);

	//获取/app1节点数据
	byte[] d1 = zk.getData("/app1", false, null);
	System.out.println("d1= "+new String(d1));
	//获取/app1/app1_1节点数据
	byte[] c1 = zk.getData("/app1/app1_1", false, null);
	System.out.println("c1= "+new String(c1));
	//获取子节点目录列表
	List<String> cList = zk.getChildren("/app1", false);
	System.out.println("cList= "+cList);

	//修改目录节点数据(如果版本为-1,那么版本自动增加)
	//zk.setData("/app1","valllll".getBytes(),-1);
	//修改子目录节点数据
	//zk.setData("/app1/app1_1","valLLLLL".getBytes(),-1);

	/*
		删除节点(如果存在子节点,不能删除父节点)
		版本如果为-1,那么删除任意版本
	 */
	zk.delete("/app1/app1_1",-1);
	zk.delete("/app1",-1);
}

3.2.2 curator操作

  • curator基本操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
@Test
public void test02() throws Exception {
	//服务器地址: 格式:ip:port ,如果是集群 ip1:port1,ip2:port2,ip3:port3
	String connectString="127.0.0.1:2181";
	/*
		重试机制
		1. 等待重试时间
		2. 重试次数
	 */
	RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000,3);
	//创建zk连接(方式一)
	//CuratorFramework client = CuratorFrameworkFactory.newClient(connectString, retryPolicy);

	//创建zk连接(方式二 Fluent风格[连缀风格的称呼])
	CuratorFramework client = CuratorFrameworkFactory.builder()
			.connectString(connectString)
			.sessionTimeoutMs(5000) //会话超时时间
			.connectionTimeoutMs(2000) //连接超时时间
			.retryPolicy(retryPolicy)
			.build();


	//启动客户端
	client.start();

	/*数据节点操作*/

	/*
		1. 创建持久节点(不设置值)
	 */
	//client.create().forPath("/app1"); //默认创建持久节点

	/*
		2. 创建持久节点(设置值)
	 */
	//client.create().forPath("/app2","我是值".getBytes());

	/*
		3. 创建顺序持久节点(使用withMode函数设置)
	 */
	//client.create().withMode(CreateMode.PERSISTENT_SEQUENTIAL).forPath("/app3","我是值3".getBytes("utf-8"));

	/*
		4. 创建持久节点(如果父节点不存在,递归创建)
	 */
	//父节点不存在创建子节点报异常(org.apache.zookeeper.KeeperException$NoNodeException: KeeperErrorCode = NoNode for /app4/app4_1)
	//client.create().forPath("/app4/app4_1","我是值4".getBytes());
	//父节点不存在创建子节点可以递归创建(creatingParentContainersIfNeeded方法可以递归创建)
	//client.create().creatingParentContainersIfNeeded().withMode(CreateMode.PERSISTENT).forPath("/app5/app5_2","我是值6".getBytes("utf-8"));


	/*
		5. 删除数据节点
	 */
	//client.delete().forPath("/app");

	/*
		6. 删除节点(递归删除其所有子节点)
	 */
	//client.delete().deletingChildrenIfNeeded().forPath("/app4");

	/*
		7. 获取节点数据
	 */
	byte[] s2 = client.getData().forPath("/app2");
	System.out.println("app2节点数据: "+new String(s2));

	/*
		8. 设置节点数据
	 */
	//更新节点数据
	//client.setData().forPath("/app2","我是值2".getBytes("utf-8"));
	//更新节点数据带版本(版本设置-1位默认值,默认递增,如果随便设置值回报异常[BadVersionException])
	//client.setData().withVersion(-1).forPath("/app2","我是修改值2".getBytes("utf-8"));

	/*
		9. 检查节点是否存在
	 */
	Stat stat = client.checkExists().forPath("/app2");
	System.out.println("当前节点详情: "+stat);

	/*
		10. 获取某个节点的所有子节点路径
	 */
	List<String> list = client.getChildren().forPath("/app5");
	list.forEach(System.out::println);

}
  • curator高级操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
@Test
public void test03() throws Exception {
	//服务器地址: 格式:ip:port ,如果是集群 ip1:port1,ip2:port2,ip3:port3
	String connectString="127.0.0.1:2181";
	/*
		重试机制
		1. 等待重试时间
		2. 重试次数
	*/
	RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000,3);
	//创建zk连接(方式二 Fluent风格[连缀风格的称呼])
	CuratorFramework client = CuratorFrameworkFactory.builder()
			.connectString(connectString)
			.sessionTimeoutMs(5000) //会话超时时间
			.connectionTimeoutMs(2000) //连接超时时间
			.retryPolicy(retryPolicy)
			.build();

	//开启客户端
	client.start();

	//给某一个节点下的所有子节点添加监听(修改节点),多次操作节点只触发一次监听
	client.getChildren().usingWatcher(new CuratorWatcher() {
		@Override
		public void process(WatchedEvent event) throws Exception {
			System.out.println("事件: "+event.toString());
		}
	}).forPath("/");

	//创建/app1节点(事件触发)
	//client.create().creatingParentContainersIfNeeded().forPath("/app1");

	//设置值(事件不触发)
	//client.setData().forPath("/app1","app1".getBytes());

	//删除节点(事件触发)
	//client.delete().forPath("/app1");
}

第五章 zookeeper的高可用

  • zk集群介绍
1
2
3
4
5
6
7
8
9
10
11
单点zk服务在正式环境很容易造成单点故障,导致服务器不可用,为了解决这个问题,需要将zk服务部署成集群,可以解决单点问题.

zk集群的设计:
	1. CAP定理之CP设计
		1.1 CPA定理概念(Consistency[一致性]、Availability[可用性]、Partition tolerance[分区容错性])
		1.2 在分布式系统中不可能保证CAP全部实现,一般提供CP或者AP服务
		1.3 zk是一个典型的CP设计
	2. 集群过半可存活(过半提供服务,低于一半主机不对外提供服务)
		2.1 集群部署节点数一般为奇数个,因为过半可存活原理偶数可能浪费机器(3个节点和4个节点是一样的)
		2.2 防止脑裂(一个zk集群[5个节点由于网络原因分成了3个和2个单独成为了各自的集群],这就是脑裂)
		2.3 过半可存活策略可以很好的解决出现脑裂造成数据不一致的问题
  • 集群配置
1
2
3
4
我们以3个实例节点作为例子部署,正常每一个节点分别在不同的机器上面,由于硬件有限,将3个节点部署到一台机器上进行测试


部署方式和但实力节点差不多,就是需要修改多份配置文件
    • zk1实例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
1. 解压zk压缩包
2. 在解压文件中创建data(在data文件中创建myid文件,并且存一个服务器编号)和log文件夹 和bin目录平级即可
3. 修改conf中的zoo_sample.cfg配置文件,重命名为zoo.cfg
4. 修改配置文件内容,内容如下:

# 心跳检测(zk服务器和zk客户端之间每隔一个tickTime检查一次是否还连着[毫秒])
tickTime=2000
# zk和zk之间(leader和follower)初始化连接时能忍受几个tickTime的个数,超过个数没有连接,剔除
initLimit=10
# zk和zk(leader和follower)之间发送消息请求和应答的时间长度多少个tickTime数量
syncLimit=5
# 数据存储位置
dataDir=../data
# 事务日志存储位置
dataLogDir=../log
# 端口号
clientPort=2181
# 集群配置 server.A=B.C.D  解释: server集群配置属性(固定值) 1:和myid中存的值相同用于表示当前zk的服务编号 B:服务器主机IP C: zk服务器之间通信端口 D:zk选举端口
server.1=127.0.0.1:2555:3555
server.2=127.0.0.1:2556:3556
server.3=127.0.0.1:2557:3557

    • zk2实例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
1. 解压zk压缩包
2. 在解压文件中创建data(在data文件中创建myid文件,并且存一个服务器编号)和log文件夹 和bin目录平级即可
3. 修改conf中的zoo_sample.cfg配置文件,重命名为zoo.cfg
4. 修改配置文件内容,内容如下:

# 心跳检测(zk服务器和zk客户端之间每隔一个tickTime检查一次是否还连着[毫秒])
tickTime=2000
# zk和zk之间(leader和follower)初始化连接时能忍受几个tickTime的个数,超过个数没有连接,剔除
initLimit=10
# zk和zk(leader和follower)之间发送消息请求和应答的时间长度多少个tickTime数量
syncLimit=5
# 数据存储位置
dataDir=../data
# 事务日志存储位置
dataLogDir=../log
# 端口号
clientPort=2182
# 集群配置 server.A=B.C.D  解释: server集群配置属性(固定值) 2:和myid中存的值相同用于表示当前zk的服务编号 B:服务器主机IP C: zk服务器之间通信端口 D:zk选举端口
server.1=127.0.0.1:2555:3555
server.2=127.0.0.1:2556:3556
server.3=127.0.0.1:2557:3557

    • zk3实例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
1. 解压zk压缩包
2. 在解压文件中创建data(在data文件中创建myid文件,并且存一个服务器编号)和log文件夹 和bin目录平级即可
3. 修改conf中的zoo_sample.cfg配置文件,重命名为zoo.cfg
4. 修改配置文件内容,内容如下:

# 心跳检测(zk服务器和zk客户端之间每隔一个tickTime检查一次是否还连着[毫秒])
tickTime=2000
# zk和zk之间(leader和follower)初始化连接时能忍受几个tickTime的个数,超过个数没有连接,剔除
initLimit=10
# zk和zk(leader和follower)之间发送消息请求和应答的时间长度多少个tickTime数量
syncLimit=5
# 数据存储位置
dataDir=../data
# 事务日志存储位置
dataLogDir=../log
# 端口号
clientPort=2183
# 集群配置 server.A=B.C.D  解释: server集群配置属性(固定值) 3:和myid中存的值相同用于表示当前zk的服务编号 B:服务器主机IP C: zk服务器之间通信端口 D:zk选举端口
server.1=127.0.0.1:2555:3555
server.2=127.0.0.1:2556:3556
server.3=127.0.0.1:2557:3557

1
将各个节点启动,然后使用客户端连接其中的某一个进行测试.

第六章 配置中心

第1节 在Java项目中时时获取zk数据

1
2
3
4
5
6
7
举例: 以京东宙斯开放平台举例,平台地址为 https://jos.jd.com/

场景说明: 
1. 我有一个项目,要从京东宙斯平台提供的接口获取数据,京东宙斯地址假设为  192.168.1.100
2. 恰逢双十一我们平台可能非常频繁的调用京东宙斯的接口,京东宙斯平台也会根据双十一调整机房的容量,调整外部应用的访问地址调整为容量更大的 192.168.1.200
3. 由于切换地址正常情况下需要我们停机器,修改项目本地配置,然后在重新启动,这样的缺点就是需要停服务.有没有一种方式可以不停服务直接进行动态的地址切换呢?
4. zk的配置中心可以帮助我们解决这个问题

1.1 案例实现一

1
2
3
4
5
1. 创建JavaWeb项目,添加zk客户端依赖
2. 将数据保存到zk节点中
3. 在项目中使用zk客户端获取zk服务器节点数据

以JavaWeb项目是ssm项目为例
  • Spring配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!--
	实例化zk客户端
	CuratorFrameworkFactory静态工厂类
	设置工厂方法 factory-method为newClient,IOC容器实例化这个工厂类时返回的对象是newClient的返回值对象
	init-method:设置初始化方法为start,工厂类实例化完成后启动客户端
-->
<bean id="client" class="org.apache.curator.framework.CuratorFrameworkFactory" factory-method="newClient" init-method="start">
	<!--zk集群地址,多个使用逗号分隔-->
	<constructor-arg index="0" value="127.0.0.1:2181"></constructor-arg>
	<!--会话超时时间-->
	<constructor-arg index="1" value="5000"></constructor-arg>
	<!--连接超时时间-->
	<constructor-arg index="2" value="3000"></constructor-arg>
	<!--重试机制-->
	<constructor-arg index="3" ref="retry"></constructor-arg>
</bean>
<!--
	重试机制
-->
<bean id="retry" class="org.apache.curator.retry.ExponentialBackoffRetry">
	<constructor-arg index="0" value="3000"></constructor-arg>
	<constructor-arg index="1" value="3"></constructor-arg>
</bean>
  • 控制器中调用
1
2
3
4
5
6
7
8
9
10
11
12
//IOC容器注入
@Autowired
private CuratorFramework client;

@RequestMapping(value = "/hello2")
public String hello2() throws Exception {
	//获取节点数据
	byte[] bytes = client.getData().forPath("/app");
	String s = new String(bytes, "utf-8");
	System.out.println("s="+s);
	return "ok";
}

1.2 案例实现二

1
2
3
4
5
6
7
8
9
10
案例一缺点就是每次都需要zk客户端去数据节点查询数据,性能较差(网络延时)
案例二将zk服务器数据节点数据缓存到内存中,如果数据节点发生变化,使用zk监听器监听并修改缓存

案例二建立在案例一 配置文件基础之上

Curator监听器是对zookeeper基本watch的包装,可以实现循环注册和监听主要涉及到三个监听器:

1. 监听某一个节点的子节点变化  --  PathChildrenCache
2. 监听特定的某一个节点        -- NodeCache
3. 监听某一个节点的所有的后代节点  -- TreeCache
  • 编写监听器

    • 监听器一(监听某一个节点的子节点变化)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.cache.PathChildrenCache;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener;
/**
 * 监听某一个点的所有子节点
 */
public class ChildrenCacheListenerTool1 {
    /**
     * 构造器
     * @param client
     * @param path
     * @throws Exception
     */
    public ChildrenCacheListenerTool1(CuratorFramework client, String path) throws Exception {
        /*
            监听某一个点下的子节点
            第一个参数: zk客户端
            第二个参数: 节点路径
            第三个参数: 如果为true缓存数据节点内容
         */
        PathChildrenCache cache = new PathChildrenCache(client, path, true);
        //开启PathChildrenCache
        cache.start();
        /*
            设置监听,创建PathChildrenCacheListener监听对象
            第一个参数: zk客户端
            第二个参数: 事件对象
         */
        PathChildrenCacheListener listener = (x,event)->{
            System.out.println("节点名称: "+event.getData().getPath());
            System.out.println("事件名称:"+event.getType()+"<<--->>节点数据: "+(event.getData()!=null ? new String(event.getData().getData(),"utf-8"):"数据为空"));
            //节点数据发生变化监听执行,将数据写到缓存中,比如写到redis
        };
        //添加监听
        cache.getListenable().addListener(listener);
    }
}
    • 监听器二(监听特定的某一个节点变化)
1
 
    • 监听器三(监听某一个节点的所有的后代节点)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.cache.NodeCache;
import org.apache.curator.framework.recipes.cache.NodeCacheListener;
/**
 * 监听某一个特定节点
 */
public class ChildrenCacheListenerTool2 {
    /**
     * 构造器
     * @param client
     * @param path
     * @throws Exception
     */
    public ChildrenCacheListenerTool2(CuratorFramework client, String path) throws Exception {
        /*
            监听某一个特定节点
            第一个参数: zk客户端
            第二个参数: 节点路径
         */
        NodeCache cache = new NodeCache(client, path);
        //开启NodeCache
        cache.start();
        //设置监听,创建NodeCacheListener监听对象
        NodeCacheListener listener = ()->{
            System.out.println("app节点名称: "+cache.getPath());
            System.out.println("app节点数据: "+(cache.getCurrentData()!=null ? new String(cache.getCurrentData().getData(),"utf-8"):"节点被删除空"));
            //节点数据发生变化监听执行,将数据写到缓存中,比如写到redis
        };
        //添加监听
        cache.getListenable().addListener(listener);
    }
}
    • 将三个监听配置到xml配置文件中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!--
	注册监听某一个点的所有子节点
-->
<bean class="io.ukoko.listeners.ChildrenCacheListenerTool1">
	<constructor-arg index="0" ref="client"></constructor-arg>
	<constructor-arg index="1" value="/"></constructor-arg>
</bean>

<!--
	注册监听某一个特定节点
-->
<bean class="io.ukoko.listeners.ChildrenCacheListenerTool2">
	<constructor-arg index="0" ref="client"></constructor-arg>
	<constructor-arg index="1" value="/app"></constructor-arg>
</bean>
<!--
	注册监听某个节点下的所有节点,包含后代节点
-->
<bean class="io.ukoko.listeners.ChildrenCacheListenerTool3">
	<constructor-arg index="0" ref="client"></constructor-arg>
	<constructor-arg index="1" value="/"></constructor-arg>
</bean>
    • 使用zkCli.sh或者zkCli.cmd 测试
1
使用zk的常见命令在客户端进行操作,查看监听是否会被执行...

第2节 Config-Toolkit可视化配置中心

2.1 Config-Toolkit简介

1
2
3
4
5
6
7
8
9
10
11
12
13
介绍: Config-Toolkit是一款由当当网开源的分布式配置管理工具,通过web界面维护属性配置和配置的导入导出功能

特性:
	1. 集中管理集群配置
	2. 实现配置热更新
	3. 多配置源支持,内置支持zookeeper、本地文件、http协议
	4. Spring集成
	5. web界面管理配置
	...

环境:
	1. JAVA 7+
	2. TOMCAT 7+ for ConfigWeb

2.2 Config-Toolkit下载地址

1
2
1. GitEE地址  : https://gitee.com/mirrors/Config-Toolkit
2. GitHub地址 : https://github.com/dangdangdotcom/config-toolkit

2.3 Config-Toolkit安装

1
2
3
4
5
1. 下载源码  git clone https://gitee.com/mirrors/Config-Toolkit.git
2. 编译源码  mvn clean package -Dmaven.test.skip=true 
3. 运行: 
	3.1 进入项目目录  cd Config-Toolkit\config-face\target
	3.2 执行运行命令  java -jar config-face.jar --zk=localhost:2181

2.4 Config-Toolkit使用

  • 访问
1
2
3
4
5
6
7
8
9
10
11
config-face项目运行起来之后 通过地址访问: http://localhost:8080  需要用户名和密码

1. 用户名/密码创建
	1.1 启动zookeeper服务器,使用zkCli.sh客户端连接上服务器
	1.2 创建/projectx节点,并赋初始化值   命令  create /projectx 1
	1.3 创建子节点create /projectx/modulex a9993e364706816aba3e25717850c26c9cd0d89d 并赋初始化值,这个初始化值是一个使用sha1加密之后的密文(本文密文为官网提供对应明文abc)
	1.4 用户名和密码创建完成(密码密文对应明文为abc,用户名为/projectx/modulex这个根节点目录)
2. 访问http://localhost:8080这个地址使用 用户名:/projectx/modulex 和 密码: abc 登录即可进入页面

下面是第一次登录成功的页面

登陆页面登陆成功初始化页面

 

  • 页面配置文件操作
1
2
3
1. 创建版本
2. 创建群组
3. 选中群组并且在群组中设置key-value键值对的配置信息

 

 

 

2.5 Config-Tools API使用

  • 添加依赖
1
2
3
4
5
<dependency>
  <groupId>com.dangdang</groupId>
  <artifactId>config-toolkit</artifactId>
  <version>3.3.2-RELEASE</version>
</dependency>
  • 通过API获取配置信息
1
2
3
4
5
6
7
8
//创建ZookeeperConfigProfile加载zk的/projectx/modulex节点的1.0版本的配置
ZookeeperConfigProfile configProfile = new ZookeeperConfigProfile("127.0.0.1:2181", "/projectx/modulex", "1.0");
//创建GeneralConfigGroup获取群组名称为group1的群组
GeneralConfigGroup group = new ZookeeperConfigGroup(configProfile,"group1");
//从群组中获取数据(通过key获取)
String jdIp = group.get("jd_ip");

支持热更新,在config-face操作页面修改配置,不需要重启应用直接获取

2.6 Config-Tools集成Spring

  • spring配置文件的头约束
1
2
3
4
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:config="https://crnlmchina.github.io/config"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
             https://crnlmchina.github.io/config https://crnlmchina.github.io/config/config.xsd">
  • 基础配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<!--
	配置Config-Toolkit
-->
<!-- 1. 基础配置 -->
<config:profile connect-str="127.0.0.1:2181" root-node="/projectx/modulex" version="1.0"></config:profile>
<!-- 
	2. 群组配置 
		node属性: 操作页面设置的群组名称
		id属性: 自定义,下面获取群组数据是需要
-->
<config:group node="group1" id="gProp"></config:group>

<!-- 
	3. 自定义一个Java类,将gProp中的数据set到自定义的类对象中 
	
	public class ZkConfig {
		private String jdIp1;
		private String jdIp2;
	}
	从群组或获取数据采用SpEL表达式方式#{goup['key']} group为<config:group> 标签中的id属性值中括号里的key是操作界面自定义的key值
-->
<bean id="zkConfig" class="io.ukoko.ZkConfig">
	<property name="jdIp1" value="#{gProp['jd_ip']}"></property>
	<property name="jdIp2" value="#{gProp['jd_ip2']}"></property>
</bean>

注意:xml配置默认不支持热更新
  • Java代码
1
2
3
4
5
6
7
8
9
//注入
@Autowired
private ZkConfig zkConfig;

@RequestMapping(value = "/hello4")
public String hello4(){
	System.out.println("zk配置信息: "+zkConfig);
	return "ok";

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/139462.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

GIS应用技巧之矢量数据编辑

挺多时候&#xff0c;需要对矢量数据进行编辑&#xff0c;那么如何编辑&#xff1f; 在ArcGIS中修改数据&#xff0c;首先要开始编辑&#xff0c;启动编辑工具条&#xff08;Editor&#xff09;。 目前编辑器处于灰色状态说明没有启动&#xff0c;那么还有些小伙伴可能在GIS主…

【项目实战】传智健康

&#x1f31f;个人博客&#xff1a;www.hellocode.top&#x1f31f; &#x1f31f;Java知识导航&#xff1a;Java-Navigate&#x1f31f; ⭐想获得更好的阅读体验请前往Java-Navigate &#x1f525;本文专栏&#xff1a;《流行框架》 &#x1f31e;如没有JavaWEB基础&#xff0…

Wireshark抓Telnet包及报文分析

Wireshark抓Telnet包及报文分析 Telnet作为应用层第二大协议&#xff0c;用途很多滴。 Telnet到底是个啥子 TELNET协议一种简单的基于文本的协议&#xff0c;它可以用来实现远程终端&#xff0c;让用户可以远程对服务器进行操作。尽管现在的远程终端基本上是基于 ssh 的了&am…

GO语言定时任务实战-gocron包

文章目录1. 基本使用1.1 初始化实例 new()1.2 添加定时任务 AddFunc()1.3 开始定时任务 Start()1.4 完整示例1.5 第一次执行定时任务的契机1.6 spec 的设置2. 粒度到秒2.1 语法示例2.2 完整示例3. 按时间间隔3.1 语法3.2 完整示例&#xff08;every&#xff09;3.3 完整示例引用…

AcWing 4721. 排队(单调栈+二分法)

问题描述 n 个小朋友排成一排&#xff0c;从左到右依次编号为 1∼n。 第 i 个小朋友的身高为 hi。 虽然队伍已经排好&#xff0c;但是小朋友们对此并不完全满意。 对于一个小朋友来说&#xff0c;如果存在其他小朋友身高比他更矮&#xff0c;却站在他右侧的情况&#xff0c…

【闲聊杂谈】纤程的概念

首先要明白几个概念&#xff1a;程序、进程、线程、纤程。 如果要非常严格的定义上来说的话&#xff0c;进程是操作系统用来做资源调度的基本单位。后来发现进程的切换是在的太费资源了&#xff0c;于是诞生了线程&#xff1b;线程多了来回切换还是很费资源&#xff0c;于是又…

[Linux]Linux项目自动化构建工具-make/Makefile

&#x1f941;作者&#xff1a; 华丞臧. &#x1f4d5;​​​​专栏&#xff1a;【LINUX】 各位读者老爷如果觉得博主写的不错&#xff0c;请诸位多多支持(点赞收藏关注)。如果有错误的地方&#xff0c;欢迎在评论区指出。 推荐一款刷题网站 &#x1f449; LeetCode刷题网站 文…

FX5U-相对定位指令DRVI(DDRVI )两种写法

该指令通过增量方式(采用相对地址的位置指定)&#xff0c;进行1速定位。 以当前停止的位置作为起点&#xff0c;指定移动方向和移动量(相对地址)进行定位动作。如果驱动触点置为ON,则输出脉冲&#xff0c;并开始从偏置速度进行加速动作。到达指令速度后&#xff0c;以指令速度进…

【LeetCode】N皇后-回溯

N皇后-回溯N皇后题目示例分析代码N皇后II题目示例分析代码总结N皇后 题目 LeetCode 51.N皇后 按照国际象棋的规则&#xff0c;皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。 n 皇后问题 研究的是如何将 n 个皇后放置在 nn 的棋盘上&#xff0c;并且使皇后彼此之间…

第二篇 - Vue 的初始化流程

一&#xff0c;前言 上篇&#xff0c;使用 rollup 完成了 Vue2 源码环境的搭建 本篇&#xff0c;介绍 Vue 的初始化流程 二&#xff0c;Vue 简介 以两个概念性问题做简单介绍 1&#xff0c;问题&#xff1a;Vue 是 MVVM 框架吗&#xff1f; 在 Vue 官网上是这样说的&#…

LeetCode450之删除二叉搜索树中的节点(相关话题:二叉搜索树,删除)

题目描述 给定一个二叉搜索树的根节点 root 和一个值 key&#xff0c;删除二叉搜索树中的 key 对应的节点&#xff0c;并保证二叉搜索树的性质不变。返回二叉搜索树&#xff08;有可能被更新&#xff09;的根节点的引用。 一般来说&#xff0c;删除节点可分为两个步骤&#x…

VS封装C++项目以及属性配置

目录 1. 封装单个项目 1.1 封装 新建C空项目&#xff0c;将需要封装的源文件(eval.cpp)和 头文件(eval.h)复制过来&#xff0c;并对它们做如下修改&#xff1a; 注&#xff1a;如果有多个头文件和源文件&#xff0c;只对可供外部调用的文件及其内部的函数作下面的处理 分别…

1.c++环境配置及第一个环境运行

开发IDE与环境 最好是使用ubuntu系统进行开发&#xff0c;如果没有的话&#xff0c;基于windows使用vs code 进行ssh连接到远程的ubuntu主机进行开发也可以。开发的过程跟本地差不多。 vs code IDE 插件的安装 1.变成中文菜单与提示,安装MS-CEINTL.vscode-language-pack-zh-…

《MySQL系列-InnoDB引擎13》文件-参数文件

文件 MySQL数据和InnoDB存储引擎表中的各种类型的文件&#xff0c;这些文件如下&#xff1a; 参数文件&#xff1a;MySQL启动时的数据库文件&#xff0c;指定初始化参数&#xff0c;介绍各种参数类型&#xff0c;以及定义某种内存结构的大小等日志文件&#xff1a;用来记录My…

C语言日常练习

这里写目录标题循环结构输入两个正整数m和n&#xff0c;求其最大公约数和最小公倍数求Snaaaaaaaaaa……的值&#xff0c;其中a是一个数字&#xff0c;n表示a的个数&#xff0c;n和a都由键盘输入一维数组从键盘输入十个数&#xff0c;并将正着输出反着输出从键盘输入十个数&…

虹科案例 | 解决ASRS系统的痛点问题居然这么简单?(上)

摘要 ASRS(自动存储和检索系统)在内部物流领域变得越来越常见。内部物流包括优化、整合、自动化和管理履行或配送中心内的货物物流流动。 ASRS穿梭机经常用在具有多个存储级别的配送中心的仓库或库存集装箱中处理散装产品的托盘。 自动化存储和检索系统的定义是专门为物料的存…

springcloud-02-微服务间通信及熔断组件

第二章 微服务间通信及熔断组件 1. 微服务间通信组件 1.1 基于RestTemplate的服务调用 Spring框架提供的RestTemplate类可用于在应用中调用rest服务&#xff0c;它简化了与http服务的通信方式&#xff0c;统一了RESTful的标准&#xff0c;封装了http链接&#xff0c; 我们只…

初识 Node.js

1、回顾与思考 1.1、浏览器中的 JavaScript 的组成部分 1.2、思考&#xff1a;为什么 JavaScript 可以在浏览器中被执行 1.3、思考&#xff1a;为什么 JavaScript 可以操作 DOM 和 BOM 1.4、浏览器中的 JavaScript 运行环境 2、Node.js 简介 2.1、什么是 Node.js Node.js…

RSA加密算法完整加密流程

RSA完整加密流程总结1.1-RSA加密介绍RSA公钥加密算法是1977年由罗纳德李维斯特&#xff08;Ron Rivest&#xff09;、阿迪萨莫尔&#xff08;Adi Shamir&#xff09;和伦纳德阿德曼&#xff08;Leonard Adleman&#xff09;一起提出的。1987年7月首次在美国公布&#xff0c;当时…

实习------Spring 框架学习

Spring 是什么&#xff08;了解&#xff09; 在不同的语境中&#xff0c;Spring 所代表的含义是不同的。下面我们就分别从“广义”和“狭义”两个角度&#xff0c;对 Spring 进行介绍。 广义上的 Spring 泛指以 Spring Framework 为核心的 Spring 技术栈。 经过十多年的发展&…