Zookeeper客户端的使用与集群特性

Zookeeper建立连接
需要注意的是,zk在main方法中运行的时候,因为zk的连接的异步进行的,是一个守护线程,如果我们主线程停止,守护线程也会跟着停止,会导致我们根本感应不到zk连接建立main方法就结束了。这里 使用countDownLatch来等待连接建立完成后再继续往下走;
在这里插入图片描述
查看到我们zk的状态为
SyncConnected:代表连接建立好了
type:None;‘
通过这些事件感知到连接已经建立好了;
代码实现如下:

private static ZooKeeper zooKeeper=null;
    private static CountDownLatch countDownLatch=new CountDownLatch(1);
    private final static  String CONNECT_STR="192.168.88.3:2181";
    private final static Integer  SESSION_TIMEOUT=30*1000;
    public static void main(String[] args) throws Exception{
        zooKeeper=new ZooKeeper(CONNECT_STR, SESSION_TIMEOUT, new Watcher() {
            @Override
            public void process(WatchedEvent event) {
                if (event.getType()== Event.EventType.None
                        && event.getState() == Event.KeeperState.SyncConnected){
                    log.info("连接已建立");
                    countDownLatch.countDown();
                }
            }
        });
        countDownLatch.await();
    }

zk创建节点及监听
在上面代码的基础上我们写一个关于创建节点,并且监听节点的改变;

MyConfig myConfig = new MyConfig();
        myConfig.setKey("anykey");
        myConfig.setName("anyName");
        ObjectMapper objectMapper=new ObjectMapper();
        byte[] bytes = objectMapper.writeValueAsBytes(myConfig);
        String s = zooKeeper.create("/myconfig", bytes, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
        Watcher watcher = new Watcher() {
            @SneakyThrows
            @Override
            public void process(WatchedEvent event) {
                if (event.getType()== Event.EventType.NodeDataChanged
                        && event.getPath()!=null && event.getPath().equals("/myconfig")){
                    log.info(" PATH:{}  发生了数据变化" ,event.getPath());
                    byte[] data = zooKeeper.getData("/myconfig", this, null);
                    MyConfig newConfig = objectMapper.readValue(new String(data), MyConfig.class);
                    log.info("数据发生变化: {}",newConfig);
                }
            }
        };
        byte[] data = zooKeeper.getData("/myconfig", watcher, null);
        MyConfig originalMyConfig = objectMapper.readValue(new String(data), MyConfig.class);
        log.info("原始数据: {}", originalMyConfig);
        TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);

重点代码理解
byte[] bytes = objectMapper.writeValueAsBytes(myConfig);
数据必须以字节的方式去传递
String s = zooKeeper.create("/myconfig", bytes, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
阻塞的方式创建节点

String s = zooKeeper.create("/myconfig", bytes, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);

ZooDefs.Ids.OPEN_ACL_UNSAFE 相当于拥有所有权限
CreateMode.PERSISTENT 创建持久化节点

event.getType()== Event.EventType.NodeDataChanged && event.getPath()!=null && event.getPath().equals("/myconfig"

当前监听的事件为NodeDataChanged ,监听的几点路径为/myconfig

//将节点添加到监听中,写了两个地方,其中一个地方在通知完成后的地方,用于当通知发放后,还可以继续监听
byte[] data = zooKeeper.getData("/myconfig", this, null);
修改节点值

Stat stat = new Stat();
byte[] data = zooKeeper.getData(first_node, false, stat);
 int version = stat.getVersion();
zooKeeper.setData(first_node, "third".getBytes(), 0);
//zooKeeper.setData(first_node, "third".getBytes(), version );

利用zooKeeper.getData(first_node, false, stat);来获取当前节点的start的详细信息;(l乐观锁)
根据当前版本号进行修改,如果版本信息不对的话,修改将不成功,防止代码并发的情况发生,在zk服务端中是不存在并发的情况的,其运行为阻塞队列式运行的。
删除节点值
跟修改节点一样操作,但是多了一个,如果传入的值为-1那么无视版本号删除;
getZooKeeper().delete("/config",-1);
异步请求

getZooKeeper().getData("/xsn", false, (rc, path, ctx, data, stat) -> {
              Thread thread = Thread.currentThread();
              log.info(" Thread Name: {},   rc:{}, path:{}, ctx:{}, data:{}, stat:{}",thread.getName(),rc, path, ctx, data, stat);},"xsn");
              log.info(" over .");

执行结果
INFO [main:BaseOperations@81] - over .
INFO [main-EventThread:BaseOperations@79] - Thread Name: main-EventThread, rc:0, path:/xsn, ctx:xsn, data:[116, 101, 115, 116], stat:73,74,1609852651729,1609852662649,1,0,0,0,4,0,73
over在上面那个日志之后打印出来的
Curator
对zk支持最好的第三方客户端;封装方法便于使用加上maven即可使用
建立连接
CuratorFramework curatorFramework = CuratorFrameworkFactory.newClient(connectString, retryPolicy);
监听
curatorFramework.getConnectionStateListenable().addListener(((client, newState) -> {
if (newState== ConnectionState.CONNECTED){
log.info(" 连接建立");
}
}));
创建节点
CuratorFramework curatorFramework = getCuratorFramework();
String path = curatorFramework.create().forPath("/curator‐node");
递归创建子节点
String pathWithParent = “/node-parent/sub-node-1”;
String path = curatorFramework.create().creatingParentsIfNeeded().forPath(pathWithParent);
获取数据
byte[] bytes = curatorFramework.getData().forPath("/curator‐node");
更新数据
curatorFramework.setData().forPath("/curator‐node",“changed!”.getBytes());
byte[] bytes = curatorFramework.getData().forPath("/curator‐node");
删除节点
curatorFramework.delete().guaranteed().deletingChildrenIfNeeded().forPath("/node‐parent");
防止异常的原因发生创建节点,在创建节点的时候先去判断一下这个节点是否已经存在了,不存在再创建
String forPath = curatorFramework
.create()
.withProtection()
.withMode(CreateMode.EPHEMERAL_SEQUENTIAL).
forPath("/curator-node", “some-data”.getBytes());
log.info(“curator create node :{} successfully.”, forPath);
zookeeper集群
Leader:负责写,读数据
Follower:负责读数据,如果Leader失联,他将参与选举,可能称为新的Leader
Observer:只负责读数据,不参与选举
leader写数据会把数据更新到follower上面,至少要更新过半以上的机子(包括本机leader)才会成功的返回给客户端,那么当机子数量多的时候,写的速度反而会比较低;所以推荐奇数台,因为在写的时候3台至少要写2台,四台却要写3台,所以一般推荐奇数台,因为3或者5都要比4总体来说好上不少。
安装zk集群
我们在一台机子上安装zk的集群:
1、修改dataDir:各种日志之类的文件路径修改,独有
2、修改端口号,与其他集群节点区别开来

在这里插入图片描述
3、添加下面这一段,用于集群节点之间的通讯,server.1(这个1,2,3,4代表的式myid,每台服务的唯一标识,没有特别的含义)配置在
dataDir文件夹里面创建一个myid文件写上对应的1,2,3,4
2001,2002,2003,2004 用于集群之间的通讯的端口号
3001,3002,3003,3004用于集群选举使用的端口号
(如果在不同的机子上可以采用一样的端口号,因为他们的ip不同是可以区别开来的)
在这里插入图片描述
我们启动的时候没办法指定那一台机子是leader,这个只能靠zk内部的选举算法实现;选举算法会尽量让拥有最新数据的那台自己称为leader;Observer是可以指定的,就上上面那个2004:3004那台机子;
启动zk集群
./bin/zkServer.sh start conf/zoo-1.cfg(分别1234启动)
查看zk集群的分配
./bin/zkServer.sh status conf/zoo-1.cfg (也是1234查看)
连接zk集群,可以选择单台连接,也可以选择把所有ip和端口号全部输入进去,zk会随机选择一台进行连接;
如果选择单台连接:那么这台服务器挂掉,我们就和zk失去了连接,通过所有ip进行连接,那么有一半以上的zk还存活着就可以和zk保持连接;
./bin/zkCli.sh -server 192.168.88.3:2181,192.168.88.3:2182,192.168.88.3:2183,192.168.88.3:2184
官方自带的集群连接方法,我们优化后
byte[] data = getZooKeeper().getData("/zookeeper", false, stat);

官方自带的方法进行自动重连处理

 while (true){
            try {
                Stat stat = new Stat();
                byte[] data = getZooKeeper().getData("/zookeeper", false, stat);
                log.info("get data : {}", new String(data));

                TimeUnit.SECONDS.sleep(5);
            }catch (Exception e){
                e.printStackTrace();
                log.info(" 开始重连......");

                while (true){
                    log.info("zookeeper status :{}",getZooKeeper().getState().name());
                    if (getZooKeeper().getState().isConnected()) {
                        break;
                    }
                    TimeUnit.SECONDS.sleep(3);
                }
            }
        }

Curator自动重连

RetryPolicy retryPolicy=new ExponentialBackoffRetry( 5*1000, 10 );
        String connectStr = "192.168.88.3:2181,192.168.88.3:2182,192.168.88.3:2183,192.168.88.3:2184";
        CuratorFramework curatorFramework = CuratorFrameworkFactory.newClient(connectStr, retryPolicy);
        curatorFramework.start();
        String pathWithParent = "/test";
        byte[] bytes = curatorFramework.getData().forPath(pathWithParent);
        System.out.println(new String(bytes));
        while (true) {
            try {
                byte[] bytes2 = curatorFramework.getData().forPath(pathWithParent);
                System.out.println(new String(bytes2));

                TimeUnit.SECONDS.sleep(5);
            } catch (Exception e) {
                e.printStackTrace();
             }
        }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值