当前位置: 首页 > news >正文

第三节 zookeeper基础应用与实战2

目录

1. Watch事件监听

1.1 一次性监听方式:Watcher

1.2 Curator事件监听机制

2. 事务&异步操作演示

2.1 事务演示

2.2 异步操作

3. Zookeeper权限控制

3.1 zk权限控制介绍

3.2 Scheme 权限模式

3.3 ID 授权对象

3.4 Permission权限类型

3.5 在控制台实现操作

3.6 Curator演示ACL的使用

4. Zookeeper集群搭建

4.1 搭建要求

4.2 Zookeeper集群角色

4.2 准备工作

4.3 配置集群

4.4 启动集群

5.Zookeeper集群操作

5.1 客户端操作zk集群

5.2 模拟集群异常操作

5.3 curate客户端连接zookeeper集群


1. Watch事件监听

1.1 一次性监听方式:Watcher

利用 Watcher 来对节点进行监听操作,可以典型业务场景需要使用可考虑,但一般情况不推荐使用。

public class CuratorWatchTest {
​private CuratorFramework client;
​/*** 建立连接*/@Beforepublic void testConnect(){
​/*** String connectString,  连接字符串 zk地址 端口: "192.168.58.100:2181,,,,"* int sessionTimeoutMs,  会话超时时间* int connectionTimeoutMs,  连接超时时间* RetryPolicy retryPolicy   重试策略*///1. 第一种方式RetryPolicy retryPolicy =new ExponentialBackoffRetry(3000,10);
​//2. 第二种方式client = CuratorFrameworkFactory.builder().connectString("192.168.58.100:2181").sessionTimeoutMs(60*1000).connectionTimeoutMs(15*1000).retryPolicy(retryPolicy).namespace("mashibing")  //当前程序创建目录的根目录.build();
​client.start();}
​/*** 演示一次性监听*/@Testpublic  void testOneListener() throws Exception {
​byte[] data = client.getData().usingWatcher(new Watcher() {@Overridepublic void process(WatchedEvent watchedEvent) {System.out.println("监听器 watchedEvent: " + watchedEvent);}}).forPath("/test");
​System.out.println("监听节点内容:" + new String(data));
​while(true){
​}}@Afterpublic void close(){client.close();}
}

上面这段代码对 /test 节点注册了一个 Watcher 监听事件,并且返回当前节点的内容。后面进行两次数据变更,实际上第二次变更时,监听已经失效,无法再次获得节点变动事件了。测试中控制台输出的信息如下:

image.png

1.2 Curator事件监听机制

ZooKeeper 原生支持通过注册Watcher来进行事件监听,但是其使用并不是特别方便需要开发人员自己反复注册Watcher,比较繁琐。

Curator引入了 Cache 来实现对 ZooKeeper 服务端事件的监听。

ZooKeeper提供了三种Watcher:

  • NodeCache : 只是监听某一个特定的节点

  • PathChildrenCache : 监控一个ZNode的子节点.

  • TreeCache : 可以监控整个树上的所有节点,类似于PathChildrenCache和NodeCache的组合

1)watch监听 NodeCache

监听数据节点本身的变化。NodeCacheListener 来完成后续处理。

public class CuratorWatchTest {/*** 演示 NodeCache : 给指定一个节点注册监听*/@Testpublic void testNodeCache() throws Exception {
​//1. 创建NodeCache对象NodeCache nodeCache = new NodeCache(client, "/app1");  //监听的是 /mashibing和其子目录app1
​//2. 注册监听nodeCache.getListenable().addListener(new NodeCacheListener() {@Overridepublic void nodeChanged() throws Exception {System.out.println("节点变化了。。。。。。");
​//获取修改节点后的数据byte[] data = nodeCache.getCurrentData().getData();System.out.println(new String(data));}});
​//3. 设置为true,开启监听nodeCache.start(true);
​while(true){
​}} 
}

image.png

NodeCache不仅可以监听节点内容变化,还可以监听指定节点是否存在。如果原本节点不存在,那么Cache就会在节点被创建时触发监听事件,如果该节点被删除,就无法再触发监听事件。

2)watch监听 PathChildrenCache

    /*** 演示 PathChildrenCache: 监听某个节点的所有子节点*/@Testpublic void testPathChildrenCache() throws Exception {
​//1.创建监听器对象 (第三个参数表示缓存每次节点更新后的数据)PathChildrenCache pathChildrenCache = new PathChildrenCache(client, "/app2", true);
​//2.绑定监听器pathChildrenCache.getListenable().addListener(new PathChildrenCacheListener() {@Overridepublic void childEvent(CuratorFramework curatorFramework, PathChildrenCacheEvent pathChildrenCacheEvent) throws Exception {System.out.println("子节点发生变化了。。。。。。");System.out.println(pathChildrenCacheEvent);
​if(PathChildrenCacheEvent.Type.CHILD_UPDATED == pathChildrenCacheEvent.getType()){//更新子节点System.out.println("子节点更新了!");//在一个getData中有很多数据,我们只拿data部分byte[] data = pathChildrenCacheEvent.getData().getData();System.out.println("更新后的值为:" + new String(data));
​}else if(PathChildrenCacheEvent.Type.CHILD_ADDED == pathChildrenCacheEvent.getType()){//添加子节点System.out.println("添加子节点!");String path = pathChildrenCacheEvent.getData().getPath();System.out.println("子节点路径为: " + path);
​}else if(PathChildrenCacheEvent.Type.CHILD_REMOVED == pathChildrenCacheEvent.getType()){//删除子节点System.out.println("删除了子节点");String path = pathChildrenCacheEvent.getData().getPath();System.out.println("子节点路径为: " + path);}}});
​//3. 开启pathChildrenCache.start();
​while(true){
​}}

image.png

  • 事件对象信息分析

PathChildrenCacheEvent{type=CHILD_UPDATED, data=ChildData{path='/app2/m1', stat=164,166,1670114647087,1670114698259,1,0,0,0,3,0,164, data=[49, 50, 51]}
}

3)watch监听 TreeCache

TreeCache相当于NodeCache(只监听当前结点)+ PathChildrenCache(只监听子结点)的结合版,即监听当前和子结点。

  /*** 演示 TreeCache: 监听某个节点的所有子节点*/@Testpublic void testCache() throws Exception {
​//1.创建监听器对象TreeCache treeCache = new TreeCache(client, "/app2");
​//2.绑定监听器treeCache.getListenable().addListener(new TreeCacheListener() {@Overridepublic void childEvent(CuratorFramework curatorFramework, TreeCacheEvent treeCacheEvent) throws Exception {System.out.println("节点变化了");System.out.println(treeCacheEvent);
​if(TreeCacheEvent.Type.NODE_UPDATED == treeCacheEvent.getType()){//更新节点System.out.println("节点更新了!");//在一个getData中有很多数据,我们只拿data部分byte[] data = treeCacheEvent.getData().getData();System.out.println("更新后的值为:" + new String(data));
​}else if(TreeCacheEvent.Type.NODE_ADDED == treeCacheEvent.getType()){//添加子节点System.out.println("添加节点!");String path = treeCacheEvent.getData().getPath();System.out.println("子节点路径为: " + path);
​}else if(TreeCacheEvent.Type.NODE_REMOVED == treeCacheEvent.getType()){//删除子节点System.out.println("删除节点");String path = treeCacheEvent.getData().getPath();System.out.println("删除节点路径为: " + path);}}});
​//3. 开启treeCache.start();
​while(true){
​}}

image.png

2. 事务&异步操作演示

2.1 事务演示

CuratorFramework 的实例包含 inTransaction( ) 接口方法,调用此方法开启一个 ZooKeeper 事务。

可以复合create、 setData、 check、and/or delete 等操作然后调用 commit() 作为一个原子操作提交。

/*** 事务操作*/
@Test
public void TestTransaction() throws Exception {
​//1. 创建Curator对象,用于定义事务操作CuratorOp createOp = client.transactionOp().create().forPath("/app3", "app1-data".getBytes());CuratorOp setDataOp = client.transactionOp().setData().forPath("/app2", "app2-data".getBytes());CuratorOp deleteOp = client.transactionOp().delete().forPath("/app2");
​//2. 添加事务操Collection<CuratorTransactionResult> results = client.transaction().forOperations(createOp, setDataOp, deleteOp);
​//3. 遍历事务操作结果for (CuratorTransactionResult result : results) {System.out.println(result.getForPath() + " - " + result.getType());}
}

2.2 异步操作

前面提到的增删改查都是同步的,但是 Curator 也提供了异步接口,引入了 BackgroundCallback 接口用于处理异步接口调用之后服务端返回的结果信息。

BackgroundCallback 接口中一个重要的回调值为 CuratorEvent,里面包含事件类型、响应码和节点的详细信息。

​// 异步操作@Testpublic void TestAsync() throws Exception {
​while(true){
​// 异步获取子节点列表GetChildrenBuilder builder = client.getChildren();builder.inBackground(new BackgroundCallback() {@Overridepublic void processResult(CuratorFramework curatorFramework, CuratorEvent curatorEvent) throws Exception {System.out.println("子节点列表:" + curatorEvent.getChildren());}}).forPath("/");
​TimeUnit.SECONDS.sleep(5);}
​}

3. Zookeeper权限控制

3.1 zk权限控制介绍

Zookeeper作为一个分布式协调框架,内部存储了一些分布式系统运行时的状态的数据,比如master选举、比如分布式锁。对这些数据的操作会直接影响到分布式系统的运行状态。因此,为了保证zookeeper中的数据的安全性,避免误操作带来的影响。Zookeeper提供了一套ACL权限控制机制来保证数据的安全。

ACL权限控制,使用:scheme:id:perm来标识。

  • Scheme(权限模式),标识授权策略

  • ID(授权对象)

  • Permission:授予的权限

ZooKeeper的权限控制是基于每个znode节点的,需要对每个节点设置权限,每个znode支持设置多种权限控制方案和多个权限,子节点不会继承父节点的权限,客户端无权访问某节点,但可能可以访问它的子节点。

3.2 Scheme 权限模式

Zookeeper提供以下权限模式,所谓权限模式,就是使用什么样的方式来进行授权。

  • world: 默认方式,相当于全部都能访问。

  • auth:代表已经认证通过的用户

    cli中可以通过 addauth digest user:pwd 来添加当前上下文中的授权用户

  • digest:即用户名:密码这种方式认证,这也是业务系统中最常用的。

    username:password 字符串来产生一个MD5串,然后该串被用来作为ACL ID。认证是通过明文发送username:password 来进行的,当用在ACL时,表达式为username:base64 ,base64是password的SHA1摘要的编码。

  • ip:通过ip地址来做权限控制

    比如 ip:192.168.1.1 表示权限控制都是针对这个ip地址的。也可以针对网段 ip:192.168.1.1/24,此时addr中的有效位与客户端addr中的有效位进行比对。

3.3 ID 授权对象

指权限赋予的用户或一个指定的实体,不同的权限模式下,授权对象不同。

Id ipId = new Id("ip", "192.168.58.100");
Id ANYONE_ID_UNSAFE = new Id("world", "anyone");

3.4 Permission权限类型

指通过权限检查后可以被允许的操作,create /delete /read/write/admin

  • Create 允许对子节点Create 操作

  • Read 允许对本节点GetChildren 和GetData 操作

  • Write 允许对本节点SetData 操作

  • Delete 允许对子节点Delete 操作

  • Admin 允许对本节点setAcl 操作

权限模式(Schema)和授权对象主要用来确认权限验证过程中使用的验证策略:

比如ip地址、digest:username:password,匹配到验证策略并验证成功后,再根据权限操作类型来决定当前客户端的访问权限。

3.5 在控制台实现操作

在Zookeeper中提供了ACL相关的命令

getAcl        getAcl <path>     读取ACL权限
setAcl        setAcl <path> <acl>     设置ACL权限
addauth      addauth <scheme> <auth>     添加认证用户

1)word方式

创建一个节点后默认就是world模式

[zk: localhost:2181(CONNECTED) 6] create /auth
Created /auth[zk: localhost:2181(CONNECTED) 7] getAcl /auth
'world,'anyone
: cdrwa[zk: localhost:2181(CONNECTED) 8] create /auth2
Created /auth2[zk: localhost:2181(CONNECTED) 9] getAcl /auth2
'world,'anyone
: cdrwa[zk: localhost:2181(CONNECTED) 10] 

其中, cdrwa,分别对应 create . delete read write admin

2)IP方式

在ip模式中,首先连接到zkServer的命令需要使用如下方式

zkCli.sh -server 127.0.0.1:2181 

接着按照IP的方式操作如下

[zk: 127.0.0.1:2181(CONNECTED) 0] create /ip-model
Created /ip-model[zk: 127.0.0.1:2181(CONNECTED) 1] setAcl /ip-model ip:127.0.0.1:cdrwa[zk: 127.0.0.1:2181(CONNECTED) 3] getAcl /ip-model
'ip,'127.0.0.1
: cdrwa

3) Auth模式

auth模式的操作如下。

[zk: 127.0.0.1:2181(CONNECTED) 5] create /spike
Created /spike[zk: 127.0.0.1:2181(CONNECTED) 6] addauth digest spike:123456[zk: 127.0.0.1:2181(CONNECTED) 9] setAcl /spike auth:spike:cdrwa[zk: 127.0.0.1:2181(CONNECTED) 10] getAcl /spike
'digest,'spike:pPeKgz2N9Xc8Um6wwnzFUMteLxk=
: cdrwa

当我们退出当前的会话后,再次连接,执行如下操作,会提示没有权限

[zk: localhost:2181(CONNECTED) 0] get /spike
Insufficient permission : /spike 

这时候,我们需要重新授权。

[zk: localhost:2181(CONNECTED) 1] addauth digest spike:123456
[zk: localhost:2181(CONNECTED) 2] get /spike
null 

4) Digest模式

使用语法,会发现使用方式和Auth模式相同

setAcl /digest digest:用户名:密码:权限

但是有一个不一样的点,密码需要用加密后的,否则无法被识别。

密码: 用户名和密码加密后的字符串。

使用下面程序生成密码

public class TestAcl {@Testpublic void createPw() throws NoSuchAlgorithmException {String up = "msb:msb";byte[] digest = MessageDigest.getInstance("SHA1").digest(up.getBytes());String encodeStr = Base64.getEncoder().encodeToString(digest);System.out.println(encodeStr);}
}

得到: 5FAC7McRhLdx0QUWsfEbK8pqwxc=

再回到client上进行如下操作

[zk: localhost:2181(CONNECTED) 14] create /digest
Created /digest[zk: localhost:2181(CONNECTED) 15] setAcl /digest digest:msb:5FAC7McRhLdx0QUWsfEbK8pqwxc=:cdrwa[zk: localhost:2181(CONNECTED) 16] getAcl /digest
'digest,'msb:5FAC7McRhLdx0QUWsfEbK8pqwxc=
: cdrwa

当退出当前会话后,需要再次授权才能访问/digest节点

[zk: localhost:2181(CONNECTED) 0] get /digest
Insufficient permission : /digest[zk: localhost:2181(CONNECTED) 1] addauth digest msb:msb[zk: localhost:2181(CONNECTED) 2] get /digest
null

3.6 Curator演示ACL的使用

接下来我们使用Curator简单演示一下ACL权限的访问操作。

public class TestAcl {private CuratorFramework client;@Testpublic void createPw() throws NoSuchAlgorithmException {String up = "msb:msb";byte[] digest = MessageDigest.getInstance("SHA1").digest(up.getBytes());String encodeStr = Base64.getEncoder().encodeToString(digest);System.out.println(encodeStr);}//1.创建连接@Beforepublic void createConnect(){client = CuratorFrameworkFactory.builder().connectString("192.168.58.100:2181").sessionTimeoutMs(5000).connectionTimeoutMs(20000).retryPolicy(new ExponentialBackoffRetry(1000, 3)).namespace("msbAcl").build();client.start();}@Testpublic void testCuratorAcl() throws Exception {//创建ID,以Digest方式认证,用户名和密码为 msb:msbId id = new Id("digest", DigestAuthenticationProvider.generateDigest("msb:msb"));//为ID对象指定权限List<ACL> acls = new ArrayList<>();acls.add(new ACL(ZooDefs.Perms.ALL,id));//创建节点 "auth",设置节点数据,并设置ACL权限String node = client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT)  // 设置节点类型是持久节点.withACL(acls,false)    //设置节点的ACL权限.forPath("/auth","hello".getBytes());   //设置节点的路径和数据System.out.println("成功创建带权限的节点: " + node);//获取刚刚创建的节点的数据byte[] bytes = client.getData().forPath(node);System.out.println("获取数据结果: " + new String(bytes));}}

上述代码执行后会报错

image.png

先删除节点

[zk: localhost:2181(CONNECTED) 6] deleteall /msbAcl

修改代码, 连接时增加授权

image.png

4. Zookeeper集群搭建

4.1 搭建要求

真实的集群是需要部署在不同的服务器上的,但是在我们测试时同时启动很多个虚拟机内存会吃不消,所以我们通常会搭建伪集群,也就是把所有的服务都搭建在一台虚拟机上,用端口进行区分。

我们这里要求搭建一个三个节点的Zookeeper集群(伪集群)。

4.2 Zookeeper集群角色

zookeeper集群中的节点有三种角色

  • Leader:处理集群的所有事务请求(增删改),集群中只有一个Leader。

  • Follower:只能处理读请求,参与Leader选举。

  • Observer:只能处理读请求,提升集群读的性能,但不能参与Leader选举。

image.png

4.2 准备工作

重新部署一台虚拟机作为我们搭建集群的测试服务器。

(1)安装JDK 【此步骤省略】。

(2)Zookeeper压缩包上传到服务器 (3)将Zookeeper解压 ,建立/usr/local/zookeeper-cluster目录,将解压后的Zookeeper复制到以下三个目录。

[root@localhost ~]# mkdir /usr/local/zookeeper-cluster[root@localhost software]# cp -r apache-zookeeper-3.7.1-bin /usr/local/zookeeper-cluster/zookeeper-1[root@localhost software]# cp -r apache-zookeeper-3.7.1-bin /usr/local/zookeeper-cluster/zookeeper-2[root@localhost software]# cp -r apache-zookeeper-3.7.1-bin /usr/local/zookeeper-cluster/zookeeper-3

(4)创建data目录 ,并且将 conf下zoo_sample.cfg 文件改名为 zoo.cfg

mkdir /usr/local/zookeeper-cluster/zookeeper-1/data
mkdir /usr/local/zookeeper-cluster/zookeeper-2/data
mkdir /usr/local/zookeeper-cluster/zookeeper-3/datamv  /usr/local/zookeeper-cluster/zookeeper-1/conf/zoo_sample.cfg  /usr/local/zookeeper-cluster/zookeeper-1/conf/zoo.cfg
mv  /usr/local/zookeeper-cluster/zookeeper-2/conf/zoo_sample.cfg  /usr/local/zookeeper-cluster/zookeeper-2/conf/zoo.cfg
mv  /usr/local/zookeeper-cluster/zookeeper-3/conf/zoo_sample.cfg  /usr/local/zookeeper-cluster/zookeeper-3/conf/zoo.cfg

(5) 配置每一个Zookeeper 的dataDir 和 clientPort 分别为:2181 2182 2183

修改/usr/local/zookeeper-cluster/zookeeper-1/conf/zoo.cfg

vim /usr/local/zookeeper-cluster/zookeeper-1/conf/zoo.cfgclientPort=2181
dataDir=/usr/local/zookeeper-cluster/zookeeper-1/data

修改/usr/local/zookeeper-cluster/zookeeper-2/conf/zoo.cfg

vim /usr/local/zookeeper-cluster/zookeeper-2/conf/zoo.cfgclientPort=2182
dataDir=/usr/local/zookeeper-cluster/zookeeper-2/data

修改/usr/local/zookeeper-cluster/zookeeper-3/conf/zoo.cfg

vim /usr/local/zookeeper-cluster/zookeeper-3/conf/zoo.cfgclientPort=2183
dataDir=/usr/local/zookeeper-cluster/zookeeper-3/data

4.3 配置集群

(1)在每个zookeeper的 data 目录下创建一个 myid 文件,内容分别是1、2、3 。这个文件就是记录每个服务器的ID

[root@localhost software]# echo 1 > /usr/local/zookeeper-cluster/zookeeper-1/data/myid
[root@localhost software]# echo 2 > /usr/local/zookeeper-cluster/zookeeper-2/data/myid
[root@localhost software]# echo 3 > /usr/local/zookeeper-cluster/zookeeper-3/data/myid   

(2)在每一个zookeeper 的 zoo.cfg配置客户端访问端口(clientPort)和集群服务器IP列表。

集群服务器IP列表如下

vim /usr/local/zookeeper-cluster/zookeeper-1/conf/zoo.cfg
vim /usr/local/zookeeper-cluster/zookeeper-2/conf/zoo.cfg
vim /usr/local/zookeeper-cluster/zookeeper-3/conf/zoo.cfgserver.1=192.168.58.200:2881:3881
server.2=192.168.58.200:2882:3882
server.3=192.168.58.200:2883:3883   

解释:server.服务器ID=服务器IP地址:服务器之间通信端口:服务器之间投票选举端口

4.4 启动集群

启动集群就是分别启动每个实例。

/usr/local/zookeeper-cluster/zookeeper-1/bin/zkServer.sh start
/usr/local/zookeeper-cluster/zookeeper-2/bin/zkServer.sh start
/usr/local/zookeeper-cluster/zookeeper-3/bin/zkServer.sh start

启动后我们查询一下每个实例的运行状态

/usr/local/zookeeper-cluster/zookeeper-1/bin/zkServer.sh status
/usr/local/zookeeper-cluster/zookeeper-2/bin/zkServer.sh status
/usr/local/zookeeper-cluster/zookeeper-3/bin/zkServer.sh status

查询第一个服务,Mode为follower表示是跟随者(从)

image.png

再查询第二个服务Mode 为leader表示是领导者(主)

image.png

查询第三个为跟随者(从)

image.png

5.Zookeeper集群操作

5.1 客户端操作zk集群

1) 第一步启动集群,启动后查看Zookeeper进程。

image.png

jps命令 作用是显示当前所有java 进程的pid 的命令,QuorumPeerMain是zookeeper集群的启动入口类

2) 客户端连接

  • 连接集群所有客户端

[root@localhost zookeeper-1]# ./bin/zkCli.sh -server 192.168.58.200:2181,192.168.58.200:2182,192.168.58.200:2183                        

image.png

  • 连接集群单个客户端

# 连接2181
[root@localhost zookeeper-1]# ./bin/zkCli.sh -server 192.168.58.200:2181 # 连接2182
[root@localhost zookeeper-1]# ./bin/zkCli.sh -server 192.168.58.200:2182# 在2181中创建节点
[zk: 192.168.58.200:2181(CONNECTED) 0] create /test2# 在2182中查询,发现数据已同步
[zk: 192.168.58.200:2182(CONNECTED) 0] ls /
[test1, test2, zookeeper]

以上两种方式的区别在于:

  • 如果只连接单个客户端,如果当前连接的服务器挂掉,当前客户端连接也会挂掉,连接失败。

  • 如果是连接所有客户端的形式,则允许集群中半数以下的服务挂掉!当半数以上服务挂掉才会停止服务,可用性更高一点!

3)集群节点信息查看

集群中的节点信息被存放在每一个节点/zookeeper/config/目录下

image.png

5.2 模拟集群异常操作

(1)首先我们先测试如果是从服务器挂掉,会怎么样

把3号服务器停掉,观察1号和2号,发现状态并没有变化

/usr/local/zookeeper-cluster/zookeeper-3/bin/zkServer.sh stop/usr/local/zookeeper-cluster/zookeeper-1/bin/zkServer.sh status
/usr/local/zookeeper-cluster/zookeeper-2/bin/zkServer.sh status

image.png

由此得出结论,3个节点的集群,从服务器挂掉,集群正常

(2)我们再把1号服务器(从服务器)也停掉,查看2号(主服务器)的状态,发现已经停止运行了。

/usr/local/zookeeper-cluster/zookeeper-1/bin/zkServer.sh stop/usr/local/zookeeper-cluster/zookeeper-2/bin/zkServer.sh status

image.png

由此得出结论,3个节点的集群,2个从服务器都挂掉,主服务器也无法运行。因为可运行的机器没有超过集群总数量的半数。

(3)我们再次把1号服务器启动起来,发现2号服务器又开始正常工作了。而且依然是领导者。

image.png

(4)我们把3号服务器也启动起来,把2号服务器停掉,停掉后观察1号和3号的状态。

/usr/local/zookeeper-cluster/zookeeper-3/bin/zkServer.sh start
/usr/local/zookeeper-cluster/zookeeper-2/bin/zkServer.sh stop/usr/local/zookeeper-cluster/zookeeper-1/bin/zkServer.sh status
/usr/local/zookeeper-cluster/zookeeper-3/bin/zkServer.sh status

image.png

发现新的leader产生了~

由此我们得出结论,当集群中的主服务器挂了,集群中的其他服务器会自动进行选举状态,然后产生新得leader 。

(5)我们再次测试,当我们把2号服务器重新启动起来启动后,会发生什么?2号服务器会再次成为新的领导吗?我们看结果

/usr/local/zookeeper-cluster/zookeeper-2/bin/zkServer.sh start/usr/local/zookeeper-cluster/zookeeper-2/bin/zkServer.sh status
/usr/local/zookeeper-cluster/zookeeper-3/bin/zkServer.sh status

我们会发现,2号服务器启动后依然是跟随者(从服务器),3号服务器依然是领导者(主服务器),没有撼动3号服务器的领导地位。

由此我们得出结论,当领导者产生后,再次有新服务器加入集群,不会影响到现任领导者。

image.png

5.3 curate客户端连接zookeeper集群

public class CuratorCluster {//zookeeper连接private final static String CLUSTER_CONNECT = "192.168.58.200:2181,192.168.58.200:2182,192.168.58.200:2183";//session超时时间private static final int sessionTimeoutMs = 60 * 1000;//连接超时时间private static final int connectionTimeoutMs = 5000;private static CuratorFramework client;public static String getClusterConnect() {return CLUSTER_CONNECT;}@Beforepublic void init(){// 重试策略RetryPolicy retryPolicy =new ExponentialBackoffRetry(3000,10);// zookeeper连接client = CuratorFrameworkFactory.builder().connectString(getClusterConnect()).sessionTimeoutMs(60*1000).connectionTimeoutMs(15*1000).retryPolicy(retryPolicy).namespace("mashibing")  //当前程序创建目录的根目录.build();// 添加监听器client.getConnectionStateListenable().addListener(new ConnectionStateListener() {@Overridepublic void stateChanged(CuratorFramework curatorFramework, ConnectionState connectionState) {System.out.println("连接成功!");}});client.start();}//创建节点public void createIfNeed(String path) throws Exception {Stat stat = client.checkExists().forPath(path);if(stat == null){String s = client.create().forPath(path);System.out.println("创建节点: " + s);}}//从集群中获取数据@Testpublic void testCluster() throws Exception {createIfNeed("/test");//每隔一段时间 获取一次数据while(true){byte[] data = client.getData().forPath("/test");System.out.println(new String(data));TimeUnit.SECONDS.sleep(5);}}
}

在集群中的任意服务器节点,为test设置数据

[zk: 192.168.58.200:2181,192.168.58.200:2182,192.168.58.200:2183(CONNECTED) 2] set /mashibing/test 12345

相关文章:

第三节 zookeeper基础应用与实战2

目录 1. Watch事件监听 1.1 一次性监听方式&#xff1a;Watcher 1.2 Curator事件监听机制 2. 事务&异步操作演示 2.1 事务演示 2.2 异步操作 3. Zookeeper权限控制 3.1 zk权限控制介绍 3.2 Scheme 权限模式 3.3 ID 授权对象 3.4 Permission权限类型 3.5 在控制台…...

C# Socket通信从入门到精通(21)——Tcp客户端判断与服务器断开连接的三种方法以及C#代码实现

前言 我们开发的tcp客户端程序在连接服务器以后,经常会遇到服务器已经关闭但是作为客户端的我们不知道,这时候应该应该有一个机制我们可以实时监测客户端和服务器已经断开连接,如果已经断开了连接,我们应该及时报警提示用户客户端和服务器已经断开连接,本文介绍三种可以监…...

vulnhub-->hacksudo-Thor靶机详细思路

目录 1. IP探测2.端口服务扫描3.网站漏洞扫描4.目录扫描5.信息分析6.破壳漏洞(Shellshock)nmap---漏洞检测CVE-2014-6271 7.nc反弹8.提权9.service提权 1. IP探测 ┌──(root㉿kali)-[~] └─# arp-scan -l Interface: eth0, type: EN10MB, MAC: 00:0c:29:10:3c:9b, IPv4: 19…...

Java外卖小程序管理系统

技术架构&#xff1a; springboot ssm mysql redis 有需要该项目的小伙伴可以私信我你的Q。 功能描述&#xff1a; 商品管理&#xff1a;新增商品、所有商品 菜单管理&#xff1a;菜单管理、菜单分类 订单管理&#xff1a;订单总览&#xff08;包括未付款、已付款、已…...

挖掘嵌入式系统在物联网和智能设备中的应用潜力

1. 物联网的发展和嵌入式系统 介绍物联网的定义和特点&#xff0c;以及其在各个领域中的应用。探讨物联网对嵌入式系统的需求&#xff0c;包括低功耗、小型化、实时性等特性&#xff0c;以及对嵌入式系统的数据处理和通信能力的要求。 2. 嵌入式系统在智能设备中的应用 分析…...

用docker 配置scala spark环境

要使用Docker配置Scala和Spark环境&#xff0c;您可以按照以下步骤进行操作。以下是一个基本的示例&#xff0c;您可能需要根据您的具体需求进行调整。 安装Docker: 在您的系统上安装Docker。您可以从Docker官方网站下载并安装适用于您操作系统的版本。 创建Dockerfile: 在您的…...

医疗处方架构设计和实现的实战经验总结

医疗处方是医生开具给患者的药物治疗建议。在现代医疗系统中&#xff0c;设计和实现一个高效而可靠的医疗处方架构至关重要。本文将介绍医疗处方架构的设计原则和关键组件&#xff0c;以及如何实现一个可扩展和安全的处方管理系统。 内容&#xff1a; 1. 引言 - 医疗处方的…...

专业140+总分410+华南理工大学811信号与系统考研经验华工电子信息与通信,真题,大纲,参考书。

23考研已经落幕&#xff0c;我也成功的上岸华工&#xff0c;回首这一年多的历程&#xff0c;也是有一些经验想和大家分享一下。 首先说一下个人情况&#xff0c;本科211&#xff0c;初试成绩400分。专业课140。 整体时间安排 对于考研&#xff0c;很重要的一环就是时间安排&…...

软件测试学习笔记-测试用例的编写

7中测试分类 按照阶段可划分单元测试、集成测试、系统测试、验收测试。代码可见度划分黑盒测试、灰盒测试、白盒测试 单元测试&#xff1a;针对源代码的测试 集成测试&#xff1a;针对接口进行测试 系统测试&#xff1a;针对功能和非功能的测试 验收测试&#xff1a;公测、内测…...

『运维备忘录』之 Kubernetes(K8S) 常用命令速查

一、简介 kubernetes&#xff0c;简称K8s&#xff0c;是用8代替名字中间的8个字符“ubernete”而成的缩写&#xff0c;是一个开源的&#xff0c;用于管理云平台中多个主机上的容器化的应用。kubernetes是基于容器技术的分布式架构解决方案&#xff0c;具有完备的集群管理能力&a…...

Android SDK 上传 Maven 喂奶级教程

最近领导给安排了个任务&#xff0c;让我把我们现有的一个 SDK 上传到 Maven 上去&#xff0c;方便客户直接用 gradle 依赖&#xff0c;不再需要拷贝 jar 和 so 了&#xff0c;此前我也看过一些相关的文章我想问题也不大&#xff0c;觉得工作量也就一两天的事情&#xff0c;主要…...

R语言绘图教程 | 双侧条形图绘制教程

写在前面 双侧条形图在我们的文章中也是比较常见的,那么这样的图形是如何绘制的呢? 以及它使用的数据类型是什么呢? 这些都是我们在绘制图形前需要掌握的,至少我们知道绘图的数据集如何准备,这样才踏出第一步。 今天的教程,我们会从数据的准备,以及数据如何整理,以及…...

ubuntu篇---ubuntu安装python3.9

ubuntu篇—ubuntu安装python3.9 在ubuntu上安装Python有两种方法:在线安装和源码编译安装。 方法1&#xff1a;使用apt在线安装 1.更新软件包列表并安装必备组件&#xff1a; $ sudo apt update $ sudo apt install software-properties-common2.将Deadsnakes PPA添加到系统…...

git初始化一个远程空仓库

目录 1. 仅做简单初始化2. 推送现有的非仓库文件夹3. 推送现有的仓库 git初始化一个远程空仓库主要有以下三种途径&#xff1a; 仅做简单初始化&#xff0c;例如添加 README.md 和 .gitignore。将现有的文件夹&#xff08;非仓库&#xff09;推送到远程仓库。将现有的仓库推送…...

装箱问题+宠物小精灵之收服+数字组合——01背包

一、装箱问题 (裸题) 有一个箱子容量为 V&#xff0c;同时有 n 个物品&#xff0c;每个物品有一个体积&#xff08;正整数&#xff09;。 要求 n 个物品中&#xff0c;任取若干个装入箱内&#xff0c;使箱子的剩余空间为最小。 输入 第一行是一个整数 V (0 < V ≤ 20000)&…...

记一次页面接口502问题:“502 Bad Gateway”

接收别人的项目进行迭代&#xff0c;项目部署到服务器上之后&#xff0c;有一个接口数据刷不出来&#xff0c;一直502 后来联想到网关的问题&#xff0c;想通过设置白名单的方式解决&#xff0c;设置之后依旧不行。 查看nginx日志发现报错&#xff1a; *169 connect() failed …...

Oracle systemstate、gdb、dbx介绍

当数据库出现严重的性能问题或者hang了的时候&#xff0c; 可能最常用的办法就是重启数据库&#xff0c;简单有效解决问题&#xff1b;但是重启后如何追踪问题的根本原因成了难题&#xff0c;很多信息随着重启也消失不见了&#xff0c;让追查问题变的十分棘手&#xff0c;这时就…...

Stable Diffusion 模型下载:RealCartoon-Anime - V10

文章目录 模型介绍生成案例案例一案例二案例三案例四案例五案例六案例七案例八案例九案例十下载地址模型介绍 这个检查点是从 RealCartoon3D 检查点分支出来的。它的目标是产生更多的“动漫”风格,因为我喜欢动漫。:)我知道有很多人做得很好(...

课时22:内置变量_字符串相关

2.4.2 字符串相关 学习目标 这一节&#xff0c;我们从 基础知识、简单实践、小结 三个方面来学习 基础知识 字符串相关的变量解析 字符串计数${#file} 获取字符串的长度字符串截取 - 语法为${var:pos:length} 表示对变量var从pos开始截取length个字符&#xff0c;pos为…...

软件应用实例分享,电玩计时计费怎么算,佳易王PS5游戏计时器系统程序教程

软件应用实例分享&#xff0c;电玩计时计费怎么算&#xff0c;佳易王PS5游戏计时器系统程序教程 一、前言 以下软件教程以 佳易王电玩计时计费管理系统软件V17.9为例说明 软件文件下载可以点击最下方官网卡片——软件下载——试用版软件下载 点击开始计时后&#xff0c;图片…...

架设游戏服务器租用价格?腾讯云和阿里云价格对比

游戏服务器租用多少钱一年&#xff1f;1个月游戏服务器费用多少&#xff1f;阿里云游戏服务器26元1个月、腾讯云游戏服务器32元&#xff0c;游戏服务器配置从4核16G、4核32G、8核32G、16核64G等配置可选&#xff0c;可以选择轻量应用服务器和云服务器&#xff0c;阿腾云atengyu…...

ag-Grid:对数据变化的单元格进行高亮显示

对单元格高亮 问:ag-grid 当 rowData 数据变化,如何对数据变化的党员个进行高亮? 解析: 在ag-Grid中,想要对数据变化的单元格进行高亮显示,你可以使用以下步骤来实现: 监听数据变化:首先,你需要监听rowData的变化。这可以通过在你的组件中观察rowData属性的变化来实…...

Oracle 几种行转列的方式 sum+decode sum+case when pivot

目录 原始数据&#xff1a; 方式一&#xff1a; 方式二&#xff1a; 方式三&#xff1a; unpivot的使用&#xff1a; 原始数据&#xff1a; 方式一&#xff1a; select t_name,sum(decode(t_item, item1, t_num, 0)) item1,sum(decode(t_item, item2, t_num, 0)) item2,s…...

[AIGC] Tomcat:一个简单 and 高效的 Java Web 服务器

Tomcat&#xff08;Tomcat Server&#xff09;是 Apache 基金会下的一个开源项目&#xff0c;它是一个简单 and 高效的 Java Web 服务器&#xff0c;支持 Servlet 2.5、JSP 2.2 和 EL 2.2 规范。Tomcat 是当今最受欢迎的 Java Web 服务器之一&#xff0c;它在 Java 世界中被广泛…...

鸿蒙(HarmonyOS)项目方舟框架(ArkUI)之Toggle组件

鸿蒙&#xff08;HarmonyOS&#xff09;项目方舟框架&#xff08;ArkUI&#xff09;之Toggle组件 一、操作环境 操作系统: Windows 10 专业版、IDE:DevEco Studio 3.1、SDK:HarmonyOS 3.1 二、Toggle组件 组件提供勾选框样式、状态按钮样式及开关样式。 子组件 仅当Toggl…...

使用耳机壳UV树脂制作私模定制耳塞有哪些选择呢?

私模定制耳塞人士的选择可以从以下几个方面考虑&#xff1a; 专业经验&#xff1a;选择有丰富经验的私模定制耳塞人士&#xff0c;能够更好地理解用户需求&#xff0c;提供更专业的建议和服务。可以通过查看其作品和客户评价来了解其经验和口碑。材料质量&#xff1a;选择使用…...

什么是集群服务器

近一段时间来&#xff0c;集群服务器被广大站长热议&#xff0c;所谓集群服务器就是指很多台服务器把它们集中在一起来进行同一种服务。集群服务器也可以由很多个的计算机并行去计算&#xff0c;这样可以获得非常高的计算速度&#xff0c;提升服务器整体的工作效. 其实我们都知…...

HCIA-HarmonyOS设备开发认证V2.0-3.轻量系统内核基础

目录 一、前言二、LiteOS-M系统概述三、内核框架3.1、CMSIS 和 POSIX 整体架构3.2、LiteOS-M内核启动流程 四、内核基础4.1、任务管理4.2、时间管理(待续)4.3、中断管理(待续)4.4、软件定时器(待续) 五、内存管理5.1、静态内存(待续)5.2、动态内存(待续) 六、内核通信机制6.1、…...

【JavaWeb】头条新闻项目实现 基本增删改查 分页查询 登录注册校验 业务功能实现 第二期

文章目录 一、为什么使用token口令二、登录注册功能2.1 登录表单提交后端代码&#xff1a; 2.2 根据token获取完整用户信息代码实现&#xff1a; 2.3 注册时用户名占用校验代码实现&#xff1a; 2.4 注册表单提交代码实现&#xff1a; 三、头条首页功能3.1 查询所有头条分类3.2…...

HiveQL——不借助任何外表,产生连续数值

注&#xff1a;参考文章&#xff1a; HiveSql一天一个小技巧&#xff1a;如何不借助其他任何外表&#xff0c;产生连续数值_hive生成连续数字-CSDN博客文章浏览阅读1.3k次。0 需求描述输出结果如下所示&#xff1a;12345...1001 问题分析方法一&#xff1a;起始值&#xff08;…...

Docker容器监控-CIG

目录 一、CIG说明 1. CAdvisor 2. InfluxDB 3. Grafana 二、环境搭建 1. 创建目录 2. 编写 docker-compose.yml 3. 检查并运行容器 三、进行测试 1. 查看 influxdb 存储服务 是否能正常访问 2. 查看 cAdvisor 收集服务能否正常访问 3. 查看 grafana 展现服务&#…...

python调用golang中函数方法

一、原因说明&#xff1a;由于simhash方法有多种实现方式&#xff0c;现python中simhash方法与golang中的不一样&#xff0c;需要两者代码生成结果保持一致&#xff0c;故采用python中的代码调用golang编译的so文件来实现。 环境配置&#xff1a;①Windows10系统要有gcc环境&a…...

Ps:颜色取样器工具

颜色取样器工具 Color Sampler Tool允许用户从图像中设置特定的颜色取样点&#xff0c;然后利用“信息”面板查看该点在不同颜色模式下&#xff08;包括&#xff1a;RGB、CMYK、Lab、灰度等&#xff09;的颜色值&#xff0c;因此可为色彩分析、颜色校正、色彩匹配等提供精确的数…...

Rust引用、借用和所有权详解

在Rust中&#xff0c;引用、借用和所有权是语言的核心概念&#xff0c;它们共同构成了内存管理的基石。本篇博客将介绍Rust中的这些概念&#xff0c;并通过具体的例子深入探讨它们的用法和优势。 所有权 Rust中的所有权是一种独特的内存管理机制&#xff0c;它规定了在特定作…...

C# 字体大小的相关问题

设置字体大小无法这么写&#xff0c; button1.Font.Size 20&#xff1b; 这个是只读属性&#xff1b; 把字体大小改为16&#xff0c; button2.Font new Font(button2.Font.Name, 16); 程序运行的时候先看一下窗体和控件的默认字体尺寸&#xff0c;都是9&#xff1b;然后点b…...

使用 Express.js 和 MySQL 构建 Web 应用程序

Express.js 是一个流行的 Node.js Web 框架&#xff0c;而 MySQL 是一种广泛使用的关系型数据库。本文将介绍如何结合使用 Express.js 和 MySQL 构建功能强大的 Web 应用程序。 一、安装和设置 首先&#xff0c;确保您已经安装了 Node.js 和 MySQL。然后&#xff0c;通过以下…...

1306. 跳跃游戏 III

经过测试&#xff0c;两种写法耗时差距10倍&#xff0c;我也不知道原因是啥 用访问次数的是更快的 class Solution { public:int n;bool dfs(vector<int>& arr, int start, vector<int>& visited){if(start<0||start>n || visited[start]1) return …...

List与数组相互转换

文章目录 将 List 转 数组List.toArra()Stream().toArray() 将 数组 转 ListLists.newArrayList()Stream.of() 将 List 转 数组 List.toArra() List<Integer> list Lists.newArrayList(1, 2, 3);// 无需转换类型 --> [1,2,3]Integer[] intArray list.toArray(new I…...

Linux openKylin(开放麒麟)系统SSH服务安装配置与公网远程连接

文章目录 前言1. 安装SSH服务2. 本地SSH连接测试3. openKylin安装Cpolar4. 配置 SSH公网地址5. 公网远程SSH连接6. 固定SSH公网地址7. SSH固定地址连接8. 结语 前言 openKylin是中国首个基于Linux 的桌面操作系统开发者平台&#xff0c;通过开放操作系统源代码的方式&#xff…...

鸿蒙(HarmonyOS)项目方舟框架(ArkUI)之AlphabetIndexer组件

鸿蒙&#xff08;HarmonyOS&#xff09;项目方舟框架&#xff08;ArkUI&#xff09;之AlphabetIndexer组件 一、操作环境 操作系统: Windows 10 专业版、IDE:DevEco Studio 3.1、SDK:HarmonyOS 3.1 二、AlphabetIndexer组件 可以与容器组件联动用于按逻辑结构快速定位容器显…...

java学习07---综合练习

飞机票 1.需求: 机票价格按照淡季旺季、头等舱和经济舱收费、输入机票原价、月份和头等舱或经济舱。 按照如下规则计算机票价格&#xff1a;旺季&#xff08;5-10月&#xff09;头等舱9折&#xff0c;经济舱8.5折&#xff0c;淡季&#xff08;11月到来年4月&#xff09;头等舱7…...

【RL】Bellman Equation (贝尔曼等式)

Lecture2: Bellman Equation State value 考虑grid-world的单步过程&#xff1a; S t → A t R t 1 , S t 1 S_t \xrightarrow[]{A_t} R_{t 1}, S_{t 1} St​At​ ​Rt1​,St1​ t t t, t 1 t 1 t1&#xff1a;时间戳 S t S_t St​&#xff1a;时间 t t t时所处的sta…...

PyTorch 2.2大更新!集成FlashAttention-2,性能提升2倍

【新智元导读】新的一年&#xff0c;PyTorch也迎来了重大更新&#xff0c;PyTorch 2.2集成了FlashAttention-2和AOTInductor等新特性&#xff0c;计算性能翻倍。 新的一年&#xff0c;PyTorch也迎来了重大更新&#xff01; 继去年十月份的PyTorch大会发布了2.1版本之后&#…...

2.9日学习打卡----初学RabbitMQ(四)

2.9日学习打卡 一.RabbitMQ 死信队列 在MQ中&#xff0c;当消息成为死信&#xff08;Dead message&#xff09;后&#xff0c;消息中间件可以将其从当前队列发送到另一个队列中&#xff0c;这个队列就是死信队列。而在RabbitMQ中&#xff0c;由于有交换机的概念&#xff0c;实…...

大数据Flume--入门

文章目录 FlumeFlume 定义Flume 基础架构AgentSourceSinkChannelEvent Flume 安装部署安装地址安装部署 Flume 入门案例监控端口数据官方案例实时监控单个追加文件实时监控目录下多个新文件实时监控目录下的多个追加文件 Flume Flume 定义 Flume 是 Cloudera 提供的一个高可用…...

【SQL高频基础题】550.游戏玩法分析IⅣ

这个SQL花了很久。但是有挺多启发的。 如果我们做不出来&#xff0c;就去看答案。 但是看完答案之后&#xff0c;不要着急就去看下一道题&#xff0c;先把这道题吃透&#xff0c;后面的题目就会更有思路。 题目&#xff1a; Table: Activity ----------------------- | Co…...

sheng的学习笔记-部署-目录

标题传送门 sheng的学习笔记-docker部署&#xff0c;原理图&#xff0c;命令&#xff0c;用idea设置docker sheng的学习笔记-docker部署&#xff0c;原理图&#xff0c;命令&#xff0c;用idea设置docker sheng的学习笔记-docker部署springboot sheng的学习笔记-docker部署spri…...

【Java】悲观锁和乐观锁有什么区别?

Java中的悲观锁和乐观锁的主要区别体现在以下几个方面&#xff1a; 加锁策略&#xff1a;悲观锁在操作数据时&#xff0c;总是假设最坏的情况&#xff0c;即认为其他线程会修改数据&#xff0c;因此在读取或操作数据时&#xff0c;会先对数据进行加锁&#xff0c;以保证数据的…...

Elasticsearch:使用查询规则(query rules)进行搜索

在之前的文章 “Elasticsearch 8.10 中引入查询规则 - query rules”&#xff0c;我们详述了如何使用 query rules 来进行搜索。这个交互式笔记本将向你介绍如何使用官方 Elasticsearch Python 客户端来使用查询规则。 你将使用 query rules API 将查询规则存储在 Elasticsearc…...

Java核心设计模式:代理设计模式

一、生活中常见的代理案例 房地产中介&#xff1a;客户手里没有房源信息&#xff0c;找一个中介帮忙商品代购&#xff1a;代理者一般有好的资源渠道&#xff0c;降低购物成本&#xff08;如海外代购&#xff0c;自己不用为了买东西出国&#xff09; 二、为什么要使用代理 对…...