zk基础—5.Curator的使用与剖析一

大纲

1.基于Curator进行基本的zk数据操作

2.基于Curator实现集群元数据管理

3.基于Curator实现HA主备自动切换

4.基于Curator实现Leader选举

5.基于Curator实现分布式Barrier

6.基于Curator实现分布式计数器

7.基于Curator实现zk的节点和子节点监听机制

8.基于Curator创建客户端实例的源码分析

9.Curator在启动时是如何跟zk建立连接的

10.基于Curator进行增删改查节点的源码分析

11.基于Curator的节点监听回调机制的实现源码

12.基于Curator的Leader选举机制的实现源码

1.基于Curator进行基本的zk数据操作

Guava is to Java what Curator is to ZooKeeper,引入依赖如下:

<dependencies>
    <dependency>
        <groupId>org.apache.curator</groupId>
        <artifactId>curator-framework</artifactId>
        <version>2.12.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.curator</groupId>
        <artifactId>curator-recipes</artifactId>
        <version>2.12.0</version>
    </dependency>
</dependencies>

Curator实现对znode进行增删改查的示例如下,其中CuratorFramework代表一个客户端实例。注意:可以通过creatingParentsIfNeeded()方法进行指定节点的级联创建。

public class CrudDemo {
    public static void main(String[] args) throws Exception {
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
        CuratorFramework client = CuratorFrameworkFactory.newClient("localhost:2181", 5000, 3000, retryPolicy);
        client.start();//启动客户端并建立连接
     
        System.out.println("已经启动Curator客户端");

        client.create()
            .creatingParentsIfNeeded()//进行级联创建
            .withMode(CreateMode.PERSISTENT)//指定节点类型
            .forPath("/my/path", "10".getBytes());//增

        byte[] dataBytes = client.getData().forPath("/my/path");//查
        System.out.println(new String(dataBytes));

        client.setData().forPath("/my/path", "11".getBytes());//改
        dataBytes = client.getData().forPath("/my/path");
        System.out.println(new String(dataBytes));

        List<String> children = client.getChildren().forPath("/my");//查
        System.out.println(children);

        client.delete().forPath("/my/path");//删
        Thread.sleep(Integer.MAX_VALUE);
    }
}

2.基于Curator实现集群元数据管理

Curator可以操作zk。比如自研了一套分布式系统类似于Kafka、Canal,想把集群运行的核心元数据都放到zk里去。此时就可以通过Curator创建一些znode,往里面写入对应的值。

写入的值推荐用json格式,比如Kafka就是往zk写json格式数据。这样,其他客户端在需要的时候,就可以从里面读取出集群元数据了。

3.基于Curator实现HA主备自动切换

HDFS、Kafka、Canal都使用了zk进行Leader选举,所以可以基于Curator实现HA主备自动切换。

HDFS的NameNode是可以部署HA架构的,有主备两台机器。如果主机器宕机了,备用的机器可以感知到并选举为Leader,这样备用的机器就可以作为新的NameNode对外提供服务。

Kafka里的Controller负责管理整个集群的协作,Kafka中任何一个Broker都可以变成Controller,类似于Leader的角色。

Canal也会部署主备两台机器,主机器挂掉了,备用机器就可以跟上去。

4.基于Curator实现Leader选举

(1)Curator实现Leader选举的第一种方式之LeaderLatch

(2)Curator实现Leader选举的第二种方式之LeaderSelector

(1)Curator实现Leader选举的第一种方式之LeaderLatch

通过Curator的LeaderLatch来实现Leader选举:

public class LeaderLatchDemo {
    public static void main(String[] args) throws Exception {
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
        CuratorFramework client = CuratorFrameworkFactory.newClient("localhost:2181", 5000, 3000, retryPolicy);
        client.start();
        
        //"/leader/latch"这其实是一个znode顺序节点
        LeaderLatch leaderLatch = new LeaderLatch(client, "/leader/latch");
        leaderLatch.start();
        leaderLatch.await();//直到等待他成为Leader再往后执行

        //类似于HDFS里,两台机器,其中一台成为了Leader就开始工作
        //另外一台机器可以通过await阻塞在这里,直到Leader挂了,自己就会成为Leader继续工作
        Boolean hasLeaderShip = leaderLatch.hasLeadership();//判断是否成为Leader
        System.out.println("是否成为leader:" + hasLeaderShip);

        Thread.sleep(Integer.MAX_VALUE);
    }
}

(2)Curator实现Leader选举的第二种方式之LeaderSelector

通过Curator的LeaderSelector来实现Leader选举如下:其中,LeaderSelector有两个监听器,可以关注连接状态。

public class LeaderSelectorDemo {
    public static void main(String[] args) throws Exception {
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
        CuratorFramework client = CuratorFrameworkFactory.newClient("localhost:2181", 5000, 3000, retryPolicy);
        client.start();

        LeaderSelector leaderSelector = new LeaderSelector(
            client,
            "/leader/election",
            new LeaderSelectorListener() {
                public void takeLeadership(CuratorFramework curatorFramework) throws Exception {
                    System.out.println("你已经成为了Leader......");
                    //在这里干Leader所有的事情,此时方法不能退出
                    Thread.sleep(Integer.MAX_VALUE);
                }
                
                public void stateChanged(CuratorFramework curatorFramework, ConnectionState connectionState) {
                    System.out.println("连接状态的变化,已经不是Leader......");
                    if (connectionState.equals(ConnectionState.LOST)) {
                        throw new CancelLeadershipException();
                    }
                }
            }
        );
        leaderSelector.start();//尝试和其他节点在节点"/leader/election"上进行竞争成为Leader
        Thread.sleep(Integer.MAX_VALUE);
    }
}

5.基于Curator实现的分布式Barrier

(1)分布式Barrier

(2)分布式双重Barrier

(1)分布式Barrier

很多台机器都可以创建一个Barrier,此时它们都被阻塞了。除非满足一个条件(setBarrier()或removeBarrier()),才能不再阻塞它们。

//DistributedBarrier
public class DistributedBarrierDemo {
    public static void main(String[] args) throws Exception {
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
        CuratorFramework client = CuratorFrameworkFactory.newClient("localhost:2181", 5000, 3000, retryPolicy);
        client.start();

        DistributedBarrier barrier = new DistributedBarrier(client, "/barrier");
        barrier.waitOnBarrier();
    }
}

(2)分布式双重Barrier

//DistributedDoubleBarrier
public class DistributedDoubleBarrierDemo {
    public static void main(String[] args) throws Exception {
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
        CuratorFramework client = CuratorFrameworkFactory.newClient("localhost:2181", 5000, 3000, retryPolicy);
        client.start();

        DistributedDoubleBarrier doubleBarrier = new DistributedDoubleBarrier(client, "/barrier/double", 10);
        doubleBarrier.enter();//每台机器都会阻塞在enter这里
        //直到10台机器都调用了enter,就会从enter这里往下执行
        //此时可以做一些计算任务

        doubleBarrier.leave();//每台机器都会阻塞在leave这里,直到10台机器都调用了leave
        //此时就可以继续往下执行
    }
}

6.基于Curator实现分布式计数器

如果真的要实现分布式计数器,最好用Redis来实现。因为Redis的并发量更高,性能更好,功能更加的强大,而且还可以使用lua脚本嵌入进去实现复杂的业务逻辑。但是Redis天生的异步同步机制,存在机器宕机导致的数据不同步风险。然而zk在ZAB协议下的数据同步机制,则不会出现宕机导致数据不同步的问题。

//SharedCount:通过一个节点的值来实现
public class SharedCounterDemo {
    public static void main(String[] args) throws Exception {
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
        CuratorFramework client = CuratorFrameworkFactory.newClient("localhost:2181", 5000, 3000, retryPolicy);
        client.start();

        SharedCount sharedCount = new SharedCount(client, "/shared/count", 0);
        sharedCount.start();

        sharedCount.addListener(new SharedCountListener() {
            public void countHasChanged(SharedCountReader sharedCountReader, int i) throws Exception {
                System.out.println("分布式计数器变化了......");
            }
            public void stateChanged(CuratorFramework curatorFramework, ConnectionState connectionState) {
                System.out.println("连接状态变化了.....");
            }
        });

        Boolean result = sharedCount.trySetCount(1);
        System.out.println(sharedCount.getCount());
    }
}

7.基于Curator实现zk的节点和子节点监听机制

(1)基于Curator

为什么出现如下报错[localhost][demo-hello][2025-07-17 15:31:29.305][ INFO][24992][localhost:2181)][o.a.z.ClientCnxn ] : [] [] [] Opening socket connection to server localhost/0:0:0:0:0:0:0:1:2181. Will not attempt to authenticate using SASL (unknown error) [localhost][demo-hello][2025-07-17 15:31:31.347][ INFO][24992][localhost:2181)][o.a.z.ClientCnxn ] : [] [] [] Socket error occurred: localhost/0:0:0:0:0:0:0:1:2181: Connection refused: no further information [localhost][demo-hello][2025-07-17 15:31:32.449][ INFO][24992][localhost:2181)][o.a.z.ClientCnxn ] : [] [] [] Opening socket connection to server localhost/127.0.0.1:2181. Will not attempt to authenticate using SASL (unknown error) [localhost][demo-hello][2025-07-17 15:31:34.464][ INFO][24992][localhost:2181)][o.a.z.ClientCnxn ] : [] [] [] Socket error occurred: localhost/127.0.0.1:2181: Connection refused: no further information [localhost][demo-hello][2025-07-17 15:31:35.302][ERROR][24992][ main][o.a.c.ConnectionState ] : [] [] [] Connection timed out for connection string (localhost:2181) and timeout (5000) / elapsed (18644) org.apache.curator.CuratorConnectionLossException: KeeperErrorCode = ConnectionLoss at org.apache.curator.ConnectionState.checkTimeouts(ConnectionState.java:225) at org.apache.curator.ConnectionState.getZooKeeper(ConnectionState.java:94) at org.apache.curator.CuratorZookeeperClient.getZooKeeper(CuratorZookeeperClient.java:117) at org.apache.curator.framework.imps.CuratorFrameworkImpl.getZooKeeper(CuratorFrameworkImpl.java:489) at org.apache.curator.framework.imps.ExistsBuilderImpl$3.call(ExistsBuilderImpl.java:237) at org.apache.curator.framework.imps.ExistsBuilderImpl$3.call(ExistsBuilderImpl.java:226) at org.apache.curator.RetryLoop.callWithRetry(RetryLoop.java:109) at org.apache.curator.framework.imps.ExistsBuilderImpl.pathInForegroundStandard(ExistsBuilderImpl.java:223)
07-18
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值