ZooKeeper 分布式配置

什么是分布式配置?

分布式配置是一种将配置信息分散存储在不同的节点或服务器上,以实现系统配置的统一管理和灵活调整的技术。在分布式系统中,配置信息可能包括系统参数、数据库连接信息、服务地址等,通过分布式配置可以实现这些信息的集中管理和动态更新。

分布式配置可以提高系统的可扩展性和可靠性,同时也能够简化系统的部署和维护工作。常见的分布式配置方案包括使用配置中心、分布式数据库或者分布式文件系统等。

ZooKeeper 分布式配置

上图中,配置中心用来几种管理配置信息,当配置信息被需改时,会主动通知各个服务,服务收到通知后从配置中心主动拉取配置信息。

为什么需要分布式配置中心?

公司需要开发一款产品,刚开始的时候,由于用户量、业务较少,仅需要一台服务器就可完成服务部署,服务部署、配置管理也非常容易。然而,随着业务的发展,单一节点的服务无法满足业务的飞速发展,后面就出现了分布式、集群的概念,到了现在就形成了微服务,这就导致服务部署、配置管理非常麻烦。

假设我们线上有很多个微服务分布在不同的服务器上,其中一个微服务,我们就叫它 “服务A”,当 “服务A” 的 IP 地址需要变更的时候,但是 “服务A” 又对很多其他的程序提供了服务,这个时候如果没有一个配置中心,每一个使用了 “服务A” 的应用程序都要做相应的 IP 地址修改,这是一个很麻烦的事情。

为了解决这个问题,我们将通过使用 ZooKeeper 来做分布式配置,原理如下图:

ZooKeeper 分布式配置

上图中,有 “服务A” 和 “服务B”,如果 “服务B” 通过 set 命令修改存储在 ZooKeeper 中的配置信息,同时由于 “服务B” 通过 watch 对该配置进行了监听,因此,“服务A” 将收到配置变更的通知,收到变更通知后,立即使用 get 从 ZooKeeper 拉取最新的配置信息。

这样就实现了,当配置发生变更时及时通知其他服务的目的,也就解决了我们上面提出的问题。

简单实现配置通知

下面将通过使用 zkcli 客户端工具来演示通知变更通知的效果,步骤如下:

(1)运行 zkcli 命令,打开 ZooKeeper 客户端,如下:

# 创建 /config 节点
[zk: localhost:2181(CONNECTED) 0] create /config {}
Created /config

# 使用 get 命令获取节点内容,并且使用 -w 选项监听该节点
[zk: localhost:2181(CONNECTED) 1] get -w /config
{}

(2)再次运行 zkcli 命令,打开另一个 ZooKeeper 客户端,如下:

# 使用 set 命令更新配置内容
[zk: localhost:2181(CONNECTED) 0] set /config {"module":"dev"}

(3)继续,观察步骤(1)创建的 ZooKeeper 客户端,如下:

[zk: localhost:2181(CONNECTED) 2]
WATCHER::

WatchedEvent state:SyncConnected type:NodeDataChanged path:/config zxid: 426

通过上面输出可知,客户端收到了一个类型为 NodeDataChanged(节点数据改变)的事件。

Java 简单实现配置中心

下面我们通过 ZooKeeper 的 Java API 来模拟修改配置和监听配置的功能,完整代码如下:

package com.hxstrive.zookeeper.distributed_config;

import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;
import java.util.Scanner;
import java.util.concurrent.CountDownLatch;

/**
 * 使用 Zk 实现配置中心
 * @author hxstrive.com
 */
public class ZkLoadConfig {
    private static ZooKeeper zooKeeper;

    public static void main(String[] args) throws Exception {
        ZkLoadConfig app = new ZkLoadConfig();
        app.init();
        app.getConfig();

        Scanner scanner = new Scanner(System.in);
        System.out.println("按任意键退出程序...");
        scanner.nextLine(); // 等待用户输入任意值
    }

    /**
     * 初始化
     * @throws Exception
     */
    private void init() throws Exception {
        CountDownLatch countDownLatch = new CountDownLatch(1);
        zooKeeper = new ZooKeeper("127.0.0.1:2181", 2000, new Watcher() {
            public void process(WatchedEvent watchedEvent) {
                System.out.println("触发了 " + watchedEvent.getType() + " 事件");
                if(watchedEvent.getState() == Event.KeeperState.SyncConnected) {
                    countDownLatch.countDown(); // 连接成功
                }

                // 监听配置发生变化
                if(watchedEvent.getType() == Event.EventType.NodeDataChanged) {
                    try {
                        System.out.println("重新拉取配置...");
                        ZkLoadConfig.this.getConfig();
                    } catch (Exception e){
                        e.printStackTrace();
                    }
                }
            }
        });
        System.out.println(zooKeeper);
        countDownLatch.await(); // 等待连接创建成功

        // 如果节点不存在,则创建节点
        Stat stat = zooKeeper.exists("/config", false);
        if(null == stat) {
            zooKeeper.create("/config", "{}".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
        }
    }

    /**
     * 获取配置信息
     * @throws Exception
     */
    private void getConfig() throws Exception {
        // 获取节点数据
        Stat stat = new Stat();
        byte[] data = zooKeeper.getData("/config", true, stat);
        System.out.println("配置信息:" + new String(data));
    }

}

运行示例,输出如下:

State:CONNECTING sessionid:0x0 local:null remoteserver:null lastZxid:0 xid:1 sent:0 recv:0 queuedpkts:0 pendingresp:0 queuedevents:0
触发了 None 事件
配置信息:{}
按任意键退出程序...

然后,使用 zkcli 工具连接到 zookeeper,如下:

C:\Users\Administrator>zkcli
Connecting to localhost:2181
...
WATCHER::

WatchedEvent state:SyncConnected type:None path:null zxid: -1
[zk: localhost:2181(CONNECTED) 0]

通过 set 命令修改 /config 节点的数据,命令如下:

[zk: localhost:2181(CONNECTED) 0] set /config "{}"

此时,继续观察示例控制台,如下:

触发了 None 事件
配置信息:{}
按任意键退出程序...
触发了 NodeDataChanged 事件
重新拉取配置...
配置信息:{}

最后,我们看看下面动态图:

ZooKeeper 分布式配置

说说我的看法
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
公众号