利用zookeeper手动实现配置中心

前言

配置一词,想必大家都不陌生。毕竟程序员总是收到这样的叮嘱:不要写死。也就是要把一些不经常变化的,可以统一维护的信息放在配置文件中,这样当配置发生变化时就不用重新编译代码。

在单机系统中,往往几个配置文件足矣。自然就是放在固定的目录下,程序启动时加载即可。即使修改配置,也不需要修改很多地方。但是,分布式系统是大势所趋,未来会作为程序员的基本技能。如果原先的系统被拆分成了很多个节点来部署,每次配置文件发生了变化,就需要把每个节点上的配置文件都修改一次。作为一个有追求的程序员,我们肯定不能干这事。

于是,我们把配置文件统一放在一个地方,配置文件修改了之后,能够及时通知所有节点重新加载。这样即使再多的节点,也只需要修改一次配置文件。这个管理统一配置的系统就是统一配置中心

本文主要用zookeeper的watch机制结合响应式API,实现简易的统一配置中心。

实现

可选的统一配置中心也不少,这里列出几个主要的,有兴趣的同学自己研究下

统一配置中心的核心功能之一就是配置变化后,及时通知所有的节点。zookeeper的watch机制正好可以满足这样的需求,所以本文利用zookeeper的watch机制来实现。不熟悉zookeeper异步非阻塞API的朋友可以先看这篇练练手:Java操作Zookeeper

整个实现过程基于响应式编程模型,所以一步步讲解不是很方便。有兴趣的同学可以把源码拷下来多运行几次,自然会慢慢弄懂。

获取对象

首先肯定要获取zookeeper对象

package com.sicimike.zk;

import com.google.common.base.Joiner;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;

import java.io.IOException;
import java.util.concurrent.CountDownLatch;

/**
 * @author sicimike
 * @create 2020-05-03 21:15
 */
public class ZookeeperUtil {

    private static final String[] ZK_SERVER = {
            "192.168.1.101:2181", "192.168.1.102:2181",
            "192.168.1.103:2181", "192.168.1.104:2181",
    };
    private static final int SESSION_TIMEOUT = 3000;
    private static final CountDownLatch latch = new CountDownLatch(1);

    private static ZooKeeper zooKeeper;

    public static ZooKeeper newInstance() throws IOException, InterruptedException {
        zooKeeper = new ZooKeeper(Joiner.on(",").join(ZK_SERVER), SESSION_TIMEOUT, new Watcher() {
            @Override
            public void process(WatchedEvent event) {
                switch (event.getState()) {
                    case SyncConnected:
                        latch.countDown();
                        break;
                    case Expired:
                        break;
                }
            }
        });
        latch.await();
        return zooKeeper;
    }
}

实现监听器

本文是利用zookeeper的watch机制来实现配置的更新通知,所以需要实现监听器来注册事件。因为这个监听器需要同时监听Znode创建、删除、以及Znode数据被修改等事件,所以需要实现三个接口。

package com.sicimike.zk;

import org.apache.zookeeper.AsyncCallback;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;

import java.util.concurrent.CountDownLatch;

/**
 * @author sicimike
 * @create 2020-05-03 21:18
 */
public class CommonWatcher implements Watcher, AsyncCallback.StatCallback, AsyncCallback.DataCallback {

    private CountDownLatch latch = new CountDownLatch(1);
    private DF17Config config;
    private ZooKeeper zooKeeper;

    public void await() throws InterruptedException {
        zooKeeper.exists(config.getPath(), this, this, "");
        latch.await();
    }

    @Override
    public void process(WatchedEvent event) {
        switch (event.getType()) {
            case None:
                System.out.println("None even");
                break;
            case NodeCreated:
                System.out.println("NodeCreated even");
                zooKeeper.getData(config.getPath(), this, this, "");
                break;
            case NodeDeleted:
                System.out.println("NodeDeleted even");
                config.setValue("");
                latch = new CountDownLatch(1);
                break;
            case NodeDataChanged:
                System.out.println("NodeDataChanged even");
                zooKeeper.getData(config.getPath(), this, this, "");
                break;
            case NodeChildrenChanged:
                System.out.println("NodeChildrenChanged even");
                break;
        }
    }

    @Override
    public void processResult(int rc, String path, Object ctx, Stat stat) {
        if (stat != null) {
            zooKeeper.getData(path, this, this, "");
        }
    }

    @Override
    public void processResult(int rc, String path, Object ctx, byte[] data, Stat stat) {
        if (data != null) {
            config.setValue(new String(data));
            latch.countDown();
        }
    }

    public void setZooKeeper(ZooKeeper zooKeeper) {
        this.zooKeeper = zooKeeper;
    }

    public void setConfig(DF17Config config) {
        this.config = config;
    }
}

核心配置

核心配置类也就是业务真正需要的类,包含了业务需要的所有配置,此处的配置内容抽象成一个String

package com.sicimike.zk;

import org.apache.zookeeper.ZooKeeper;

import java.io.IOException;
import java.util.Objects;

/**
 * 配置文件
 *
 * @author sicimike
 * @create 2020-05-03 21:21
 */
public class DF17Config {

    private static final String CONFIG_PATH = "/DF-config";

    private String path;
    private String value;

    public static void getConfig() throws InterruptedException, IOException {
        ZooKeeper zooKeeper = ZookeeperUtil.newInstance();
        DF17Config config = new DF17Config();
        config.setPath(CONFIG_PATH);

        CommonWatcher commonWatcher = new CommonWatcher();
        commonWatcher.setZooKeeper(zooKeeper);
        commonWatcher.setConfig(config);

        commonWatcher.await();

        while (true) {
            if (Objects.equals(config.getValue(), "")) {
                commonWatcher.await();
            }
            System.out.println("config: " + config.getValue());
            Thread.sleep(3000);
        }
    }

    public void setPath(String path) {
        this.path = path;
    }

    public String getPath() {
        return path;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }
}

测试类

package com.sicimike.zk;

import java.io.IOException;

/**
 * @author sicimike
 * @create 2020-05-03 12:05
 */
public class ZookeeperDemo {

    public static void main(String[] args) throws IOException, InterruptedException {
        DF17Config.getConfig();
    }
}

执行

执行ZookeeperDemo类,等待程序连上zookeeper之后,再利用zkCli客户端操作产生事件,查看程序的输出。操作的目录为/DF-config

NodeCreated even
config: hello DF-17
config: hello DF-17
config: hello DF-17
NodeDataChanged even
config: hello DF-18
config: hello DF-18
config: hello DF-18
NodeDeleted even
NodeCreated even
config: hello DF-17
config: hello DF-17
config: hello DF-17

此处博主先执行

create /DF-config "hello DF-17"

可以看到程序检测到了NodeCreated even事件,并且输出了最新的配置。然后执行

set /DF-config "hello DF-18"

程序检测到了NodeDataChanged even事件,并输出了最新的配置。接着执行

delete /DF-config

程序检测到了NodeDeleted even事件,并被阻塞,停止了输出,因为删除节点,相当于配置也丢失了。
最后再次创建这个Znode,程序依然可以继续执行。

总结

本文利用zookeeper简单实现了配置中心的核心功能,主要是为了熟悉zookeeper的核心API以及初步尝试响应式编程。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
配置ZooKeeper实现进程的主备选举,你可以按照以下步骤进行操作: 1. 安装ZooKeeper:首先,确保你已经成功安装了ZooKeeper,并在多个服务器上设置了相同的ZooKeeper配置。 2. 配置ZooKeeper:在每个服务器的ZooKeeper配置文件(通常是`zoo.cfg`)中,确保你设置了相同的`dataDir`(数据目录)和`clientPort`(客户端端口)。 3. 配置服务器标识:在每个服务器的配置文件中,使用独特的`server.id`标识符指定每个服务器的编号。例如,对于三个服务器,你可以分别将它们配置为`server.1`、`server.2`和`server.3`。 4. 配置选举算法:在每个服务器的配置文件中,将选举算法设置为`3`。这将使用ZooKeeper的默认选举算法,即基于Paxos的选举机制。 5. 配置集群连接:在每个服务器的配置文件中,使用以下格式配置集群连接:`server.id=hostname:port:port`。其中,`hostname`是服务器的主机名或IP地址,第一个`port`是服务器之间通信的端口,第二个`port`是选举通信的端口。 6. 启动ZooKeeper集群:在每个服务器上启动ZooKeeper实例,并确保它们能够相互连接和通信。 7. 监控选举状态:你可以使用ZooKeeper提供的命令行工具或API来监控选举状态。例如,你可以使用`stat`命令查看当前ZooKeeper集群的领导者和参与者信息。 通过以上步骤,你可以配置ZooKeeper实现进程的主备选举。请记住,选举是自动进行的,当主节点故障或失去连接时,备节点将会自动选举出新的主节点。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值