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

news2024/10/6 6:41:32

目录

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;
​
    /**
     * 建立连接
     */
    @Before
    public 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();
    }
​
    /**
     * 演示一次性监听
     */
    @Test
    public  void testOneListener() throws Exception {
​
        byte[] data = client.getData().usingWatcher(new Watcher() {
            @Override
            public void process(WatchedEvent watchedEvent) {
                System.out.println("监听器 watchedEvent: " + watchedEvent);
            }
        }).forPath("/test");
​
        System.out.println("监听节点内容:" + new String(data));
​
        while(true){
​
        }
    }
  
    @After
    public 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 : 给指定一个节点注册监听
     */
    @Test
    public void testNodeCache() throws Exception {
​
        //1. 创建NodeCache对象
        NodeCache nodeCache = new NodeCache(client, "/app1");  //监听的是 /mashibing和其子目录app1
​
        //2. 注册监听
        nodeCache.getListenable().addListener(new NodeCacheListener() {
            @Override
            public 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: 监听某个节点的所有子节点
     */
    @Test
    public void testPathChildrenCache() throws Exception {
​
        //1.创建监听器对象 (第三个参数表示缓存每次节点更新后的数据)
        PathChildrenCache pathChildrenCache = new PathChildrenCache(client, "/app2", true);
​
        //2.绑定监听器
        pathChildrenCache.getListenable().addListener(new PathChildrenCacheListener() {
            @Override
            public 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: 监听某个节点的所有子节点
     */
    @Test
    public void testCache() throws Exception {
​
        //1.创建监听器对象
        TreeCache treeCache = new TreeCache(client, "/app2");
​
        //2.绑定监听器
        treeCache.getListenable().addListener(new TreeCacheListener() {
            @Override
            public 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,里面包含事件类型、响应码和节点的详细信息。

​
    // 异步操作
    @Test
    public void TestAsync() throws Exception {
​
        while(true){
​
            // 异步获取子节点列表
            GetChildrenBuilder builder = client.getChildren();
            builder.inBackground(new BackgroundCallback() {
                @Override
                public 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 {

    @Test
    public 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;

    @Test
    public 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.创建连接
    @Before
    public 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();
    }

    @Test
    public void testCuratorAcl() throws Exception {

        //创建ID,以Digest方式认证,用户名和密码为 msb:msb
        Id 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/data

mv  /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.cfg

clientPort=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.cfg

clientPort=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.cfg

clientPort=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.cfg

server.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;
    }

    @Before
    public 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() {
            @Override
            public 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);
        }
    }


    //从集群中获取数据
    @Test
    public 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

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

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

相关文章

antdpro框架npm install 报错,切换tyarn安装成功。

报错日志 有时间补 当前版本 解决办法 进入工作目录 安装官方推荐的tyarn工具&#xff1a;npm install yarn tyarn -g 进行依赖安装&#xff1a;tyarn 启动项目 &#xff1a;tyarn start 注意&#xff1a; 技术迭代较快&#xff0c;建议查询官网后实践&#xff0c;以上作为…

Hive窗口函数详解

一、 窗口函数知识点 1.1 窗户函数的定义 窗口函数可以拆分为【窗口函数】。窗口函数官网指路&#xff1a; LanguageManual WindowingAndAnalytics - Apache Hive - Apache Software Foundationhttps://cwiki.apache.org/confluence/display/Hive/LanguageManual%20Windowing…

并行计算导论 笔记 1

目录 并行编程平台隐式并行超标量执行/指令流水线超长指令字处理器 VLIW 内存性能系统的局限避免内存延迟的方法 并行计算平台控制结构通信模型共享地址空间平台消息传递平台对比 物理组织理想并行计算机并行计算机互联网络网络拓朴结构基于总线的网络交叉开关网络多级网络全连…

微服务架构RabbitMQ实现CQRS模式

在现代软件开发中,微服务架构和CQRS模式都是备受关注的技术趋势。微服务架构通过将应用程序拆分为一系列小型、自治的服务,提供了更好的可伸缩性和灵活性。而CQRS模式则通过将读操作和写操作分离,优化了系统的性能和可维护性。本文小编将为大家介绍如何在ASP.NET Core微服务…

树莓派4B(Raspberry Pi 4B)使用docker搭建阿里巴巴sentinel服务

树莓派4B&#xff08;Raspberry Pi 4B&#xff09;使用docker搭建阿里巴巴sentinel服务 由于国内访问不了docker hub&#xff0c;而国内镜像仓库又没有适配树莓派ARM架构的sentinel镜像&#xff0c;所以我们只能退而求其次——自己动手构建镜像。本文基于Ubuntu&#xff0c;Jav…

Netty应用(八) 之 ByteBuf 半包粘包问题 半包粘包解决方案-封帧解码器

目录 19.ByteBuf 19.1 ByteBuf的基本使用 19.2 ByteBuf的扩容机制 19.3 ByteBuf与内存的关系 19.4 ByteBuf的内存结构 19.5 ByteBuf的API 19.5.1 ByteBuf的写操作 19.5.2 ByteBuf的读操作 19.5.3 ByteBuf的slice 19.6 ByteBuf的内存释放 19.6.1 实现API 19.6.2 如何…

前沿重器[42] | self-RAG-大模型决策的典型案例探究

前沿重器 栏目主要给大家分享各种大厂、顶会的论文和分享&#xff0c;从中抽取关键精华的部分和大家分享&#xff0c;和大家一起把握前沿技术。具体介绍&#xff1a;仓颉专项&#xff1a;飞机大炮我都会&#xff0c;利器心法我还有。&#xff08;算起来&#xff0c;专项启动已经…

Junit5基础教程

文章目录 一&#xff0c;导入依赖二&#xff0c;基本功能一、常用断言二、执行顺序和常用注解1、通过BeforeAll类的注解来保证顺序2、通过order注解来保证执行顺序 三、依赖测试四、参数化测试五、测试套件SelectPackages、IncludePackages、SelectClasses、IncludeTags等注解的…

javaweb物业管理系统jsp项目

文章目录 物业管理系统一、系统演示二、项目介绍三、系统部分功能截图四、部分代码展示五、底部获取项目源码&#xff08;9.9&#xffe5;带走&#xff09; 物业管理系统 可用作javaweb项目、servlet项目、jsp项目的项目设计 一、系统演示 物业管理系统 二、项目介绍 语言&a…

春晚魔术和约瑟夫问题

春晚的魔术实际上是一个约瑟夫问题&#xff0c;最终的结果是魔术开始时确定的几个变量确定好的&#xff0c;扑克牌只是道具和障眼法。网上一查这个问题发现颇有历史渊源&#xff0c;17世纪的法国数学家加斯帕在《数目的游戏问题》中讲了这样一个故事&#xff1a;15个教徒和15 个…

酷开科技荣获“消费者服务之星”称号后的未来展望

恭喜酷开科技荣获2023年第四季度黑猫平台“消费者服务之星”称号&#xff01;这是对酷开科技长期以来坚持用户至上、用心服务的肯定和认可。作为OTT行业的佼佼者&#xff0c;酷开科技一直秉承着“以用户为中心”的服务理念&#xff0c;不断追求卓越品质&#xff0c;为用户提供更…

《CSS 简易速速上手小册》第6章:高级 CSS 技巧(2024 最新版)

文章目录 6.1 使用 CSS 变量进行设计&#xff1a;魔法配方的调配6.1.1 基础知识6.1.2 重点案例&#xff1a;创建可定制的主题6.1.3 拓展案例 1&#xff1a;响应式字体大小6.1.4 拓展案例 2&#xff1a;使用 CSS 变量创建动态阴影效果 6.2 calc(), min(), max() 等函数的应用&am…

Java 基于微信小程序的电子商城购物系统

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12W、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

使用R语言fifer包进行分层采样

使用R语言fifer包中的stratified()函数用来进行分层采样非常方便&#xff0c;但fifer包已经从CRAN存储库中删除&#xff0c;需要从存档中下载可用的历史版本&#xff0c;下载链接&#xff1a;Index of /src/contrib/Archive/fifer (r-project.org)https://cran.r-project.org/s…

架构篇34:深入理解微服务架构 - 银弹 or 焦油坑?

文章目录 微服务与 SOA 的关系微服务的陷阱小结 微服务是近几年非常火热的架构设计理念&#xff0c;大部分人认为是 Martin Fowler 提出了微服务概念&#xff0c;但事实上微服务概念的历史要早得多&#xff0c;也不是 Martin Fowler 创造出来的&#xff0c;Martin 只是将微服务…

Github 2024-02-11 开源项目日报Top10

根据Github Trendings的统计&#xff0c;今日(2024-02-11统计)共有10个项目上榜。根据开发语言中项目的数量&#xff0c;汇总情况如下&#xff1a; 开发语言项目数量Python项目4非开发语言项目2C项目1C项目1Solidity项目1JavaScript项目1Rust项目1HTML项目1 免费服务列表 | f…

KingSCADA实现按钮点击效果

哈喽&#xff0c;你好啊&#xff0c;我是雷工&#xff01; 在做SCADA项目的时候&#xff0c;按钮是不可缺少的功能&#xff0c;但软件自带的按钮太丑&#xff0c;已经无法满足现如今客户对界面美观度的要求。 这时候就需要UI小姐姐设计美观大气的SCADA界面&#xff0c;但UI设计…

内网安全-内网穿透

目录 内网渗透 Nc使用详解 Nc监听和探测 Nc传文件 termite内网穿透工具 ssh代理内网穿透 ssh配置socket代理 MSF多级网络穿透 内网渗透 Nc使用详解 Nc监听和探测 Nc传文件 termite内网穿透工具 1、termite 之前叫ew &#xff08;可以进行正向连接&#xff0c;可以…

[ai笔记3] ai春晚观后感-谈谈ai与艺术

欢迎来到文思源想的ai空间&#xff0c;这是技术老兵重学ai以及成长思考的第3篇分享&#xff01; 今天我们不聊技术&#xff0c;只聊感受&#xff01; 1 关于ai春晚 期待许久的ai春晚&#xff0c;但是等初一晚上观看的时候&#xff0c;或多或少还是有些失望。 首先是观看人数…

【java】笔记10:类与对象——本章练习

题目1&#xff1a; 代码如下&#xff1a; import java.util.Scanner; public class Input{public static void main(String[]args){Circle cnew Circle();PassObject yuannew PassObject();System.out.println("r""\t""times");yuan.printAreas…