Zookeeper如何实现分布式服务配置中心详解

yizhihongxing

Zookeeper如何实现分布式服务配置中心详解

什么是Zookeeper

Zookeeper是一个典型的分布式数据一致性解决方案,是Google Chubby在开源领域的实现,提供了分布式应用系统的协调服务,如配置维护、命名服务、同步服务、组服务等。

Zookeeper作为服务配置中心的应用

服务配置中心是比较常用的分布式架构中的一部分,它的目的是帮助我们实现应用程序的配置管理。Zookeeper正是通过其节点、事件机制等特性来支持分布式服务的协调和应用配置的管理。

Zookeeper实现分布式服务配置中心

配置项的注册和发现

通过节点机制,Zookeeper实现了配置项的注册和发现。我们可以将配置项写入到Zookeeper的特定节点下,通过监控该节点路径实现节点值变化的监听,使配置变化得到及时的通知。应用程序在启动时可以对Zookeeper的节点路径进行监听,当Zookeeper节点的值变化时,可以及时获取变化的值。

下面通过Java程序示例说明:

  • 注册配置项
public class ConfigCenter {
    private ZooKeeper zooKeeper;
    private static String CONFIG_ROOT_NODE_PATH = "/config-center";
    private String configNodePath;

    public ConfigCenter(String connectString, int sessionTimeout) throws IOException, KeeperException, InterruptedException {
        this.zooKeeper = new ZooKeeper(connectString, sessionTimeout, null);
        if (zooKeeper.exists(CONFIG_ROOT_NODE_PATH, false) == null) {
            zooKeeper.create(CONFIG_ROOT_NODE_PATH, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
        }
    }

    /**
     * 注册配置项
     * @param configName 配置项名称
     * @param configValue 配置项值
     * @return 配置项在Zookeeper中的节点路径
     * @throws KeeperException
     * @throws InterruptedException
     */
    public String registerConfig(String configName, String configValue) throws KeeperException, InterruptedException {
        configNodePath = zooKeeper.create(CONFIG_ROOT_NODE_PATH + "/" + configName, configValue.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
        return configNodePath;
    }
}
  • 监听配置项变化
public class ConfigCenterWatcher implements Watcher {
    private ZooKeeper zooKeeper;
    private String configNodePath;

    public ConfigCenterWatcher(ZooKeeper zooKeeper, String configNodePath) {
        this.zooKeeper = zooKeeper;
        this.configNodePath = configNodePath;
    }

    @Override
    public void process(WatchedEvent event) {
        switch (event.getType()) {
            case NodeDataChanged:
                try {
                    byte[] data = zooKeeper.getData(configNodePath, this, null);
                    System.out.println("配置项已经更新,新值为:" + new String(data));
                } catch (KeeperException | InterruptedException e) {
                    e.printStackTrace();
                }
                break;
            default:
                break;
        }
    }
}
  • 使用示例
public class ConfigCenterTest {
    private static String CONNECT_STRING = "localhost:2181";
    private static int SESSION_TIMEOUT = 10000;
    private static String CONFIG_NAME = "testConfig";
    private static String CONFIG_VALUE = "testConfigValue";

    @Test
    public void testConfigCenter() throws IOException, KeeperException, InterruptedException, InterruptedException {
        ConfigCenter configCenter = new ConfigCenter(CONNECT_STRING, SESSION_TIMEOUT);
        String configNodePath = configCenter.registerConfig(CONFIG_NAME, CONFIG_VALUE);

        ZooKeeper zooKeeper = new ZooKeeper(CONNECT_STRING, SESSION_TIMEOUT, new ConfigCenterWatcher(zooKeeper, configNodePath));
        byte[] data = zooKeeper.getData(configNodePath, true, null);
        System.out.println("配置项初始值为:" + new String(data));

        Thread.sleep(Long.MAX_VALUE);
    }
}

该示例通过Zookeeper实现了配置项注册和变化时的通知。

分布式应用系统中的服务发现

Zookeeper可以帮助我们实现分布式应用系统中的服务发现。在分布式系统中,服务可用性监控和负载均衡都是必须的特性。Zookeeper作为注册中心能够协调分布式应用系统中的服务注册和发现,也就是说,实现了单台服务器到集群间的扩展,让分布式服务可以向Zookeeper注册自己的服务地址和相关信息,也可以从Zookeeper查询服务注册信息。

下面通过Java程序示例说明:

  • 服务注册
public class ServerRegistry {
    private ZooKeeper zooKeeper;
    private static String SERVER_NODE_PATH = "/server";

    public ServerRegistry(String connectString, int sessionTimeout) throws IOException, KeeperException, InterruptedException {
        this.zooKeeper = new ZooKeeper(connectString, sessionTimeout, null);
        if (zooKeeper.exists(SERVER_NODE_PATH, false) == null) {
            zooKeeper.create(SERVER_NODE_PATH, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
        }
    }

    /**
     * 服务注册
     * @param serverName 服务名
     * @param serverAddress 服务地址
     * @throws KeeperException
     * @throws InterruptedException
     */
    public void registerServer(String serverName, String serverAddress) throws KeeperException, InterruptedException {
        String serverNodePath = SERVER_NODE_PATH + "/" + serverName;
        if (zooKeeper.exists(serverNodePath, false) == null) {
            zooKeeper.create(serverNodePath, serverAddress.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
            System.out.println(serverName + "已注册,地址为:" + serverAddress);
        }
    }
}
  • 服务发现
public class LoadBalance {
    private ZooKeeper zooKeeper;
    private static String SERVER_NODE_PATH = "/server";
    private String serviceName;

    public LoadBalance(String connectString, int sessionTimeout, String serviceName) throws IOException, KeeperException, InterruptedException {
        this.zooKeeper = new ZooKeeper(connectString, sessionTimeout, null);
        if (zooKeeper.exists(SERVER_NODE_PATH, false) == null) {
            throw new RuntimeException("no server to use");
        }
        this.serviceName = serviceName;
    }

    /**
     * 选择服务器
     * @return 服务器地址
     * @throws KeeperException
     * @throws InterruptedException
     */
    public String selectServer() throws KeeperException, InterruptedException {
        String serverNodePath = SERVER_NODE_PATH + "/" + serviceName;
        List<String> servers = zooKeeper.getChildren(serverNodePath, true);
        String serverAddress = null;
        if (servers != null && servers.size() > 0) {
            if (servers.size() == 1) {
                serverAddress = new String(zooKeeper.getData(serverNodePath + "/" + servers.get(0), true, null));
                System.out.println("唯一服务器地址为:" + serverAddress);
            } else {
                int index = ThreadLocalRandom.current().nextInt(servers.size());
                serverAddress = new String(zooKeeper.getData(serverNodePath + "/" + servers.get(index), true, null));
                System.out.println("随机选择的服务器地址为:" + serverAddress);
            }
        }
        return serverAddress;
    }
}
  • 使用示例
public class ServerRegistryTest {
    private static String CONNECT_STRING = "localhost:2181";
    private static int SESSION_TIMEOUT = 10000;
    private static String SERVER_NAME = "testServer";
    private static String SERVER_ADDRESS_1 = "192.168.1.100:8080";
    private static String SERVER_ADDRESS_2 = "192.168.1.101:8080";
    private static String SERVICE_NAME = "testService";

    @Test
    public void testServerRegistry() throws IOException, KeeperException, InterruptedException {
        ServerRegistry serverRegistry = new ServerRegistry(CONNECT_STRING, SESSION_TIMEOUT);
        serverRegistry.registerServer(SERVER_NAME, SERVER_ADDRESS_1);
        serverRegistry.registerServer(SERVER_NAME, SERVER_ADDRESS_2);

        Thread.sleep(Long.MAX_VALUE);
    }

    @Test
    public void testLoadBalance() throws IOException, KeeperException, InterruptedException {
        LoadBalance loadBalance = new LoadBalance(CONNECT_STRING, SESSION_TIMEOUT, SERVICE_NAME);
        while (true) {
            try {
                String serverAddress = loadBalance.selectServer();
                System.out.println(serverAddress);
            } catch (Exception e) {
                System.out.println("no server to use");
                Thread.sleep(2000);
            }
        }
    }
}

这个示例通过Zookeeper实现了服务注册和发现,达到了负载均衡的效果。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Zookeeper如何实现分布式服务配置中心详解 - Python技术站

(0)
上一篇 2023年5月22日
下一篇 2023年5月22日

相关文章

  • MySQL数据库备份与恢复方法

    MySQL数据库备份与恢复方法 MySQL是一款广泛使用的关系型数据库管理系统,其数据备份与恢复是非常重要的操作,本文将介绍如何备份与恢复MySQL数据库。 备份MySQL数据库 使用mysqldump命令备份 打开终端或命令提示符,并登录到MySQL服务器: mysql -uroot -p 输入密码并登录到MySQL服务器。 执行以下命令来备份数据库: m…

    database 2023年5月22日
    00
  • Linux下mysql的root密码修改方法

    下面是详细讲解“Linux下mysql的root密码修改方法”的完整攻略。 1. 前置条件 在修改MySQL密码之前,请确保已经安装了MySQL,并且已经用root用户登录MySQL。 2. 修改MySQL root密码的步骤 2.1 进入MySQL 首先,需要用管理员身份进入MySQL,可以使用如下命令: mysql -u root -p 其中,“-u r…

    database 2023年5月22日
    00
  • 学习 C++能带给我们什么

    学习C++能够带给我们很多的技能和知识,下面我详细讲解一下学习C++的完整攻略,包括以下几个方面的内容: 一、什么是C++? C++是一种通用的、静态的、编译式的、跨平台的计算机程序设计语言。C++中包含了C语言的所有特性,加上了类和模板的特性,使得C++能够更好地进行面向对象的编程和泛型编程。C++被广泛地应用在操作系统、游戏开发、应用软件、嵌入式系统、大…

    database 2023年5月22日
    00
  • 数据仓库的特点和功能

    下面是数据仓库的特点和功能的完整攻略。 数据仓库的特点 数据集中:数据仓库是将企业内部各种分散的数据集中存储于一个特定的数据库中。这样做既方便管理、维护,也使得数据易于查询和分析。 面向主题:数据仓库以主题为基本构成单位,这是与传统的事务处理系统和关系数据库最明显的不同之处。在数据仓库中,同一主题的数据要集中存放,便于查询和处理。 面向历史:数据仓库一般不仅…

    database 2023年3月27日
    00
  • oracle数据库ORA-01196错误解决办法分享

    Oracle数据库ORA-01196错误解决办法分享 问题描述 当Oracle数据库出现ORA-01196错误时,多数情况下是由于操作系统中文件权限等原因引起的。该错误信息的具体描述为: ORA-01196: file 1 is inconsistent due to a failed media recovery session. See error be…

    database 2023年5月19日
    00
  • MySQL数据备份方法的选择与思考

    MySQL数据备份方法的选择与思考 前言 数据备份在数据库使用过程中是非常重要的,一旦数据发生错误或者被损坏,备份数据可以帮助我们快速恢复数据,避免了数据的丢失和业务停顿的时间。 MySQL提供了多种数据备份方案,不同的备份方案有着不同的优点和缺点,我们应该根据具体情况选择最适合自己的备份方案。本文将介绍MySQL数据备份的常用方案和优缺点,以及如何进行备份…

    database 2023年5月21日
    00
  • Mysql中的排序规则utf8_unicode_ci、utf8_general_ci的区别总结

    Mysql中的排序规则utf8_unicode_ci、utf8_general_ci的区别总结 一、背景 在使用 MySQL 数据库时,对于中文等非拉丁字符集的排序,我们通常会使用 utf8_general_ci 或者 utf8_unicode_ci 这两种常见的排序规则。 然而,这两种排序规则有什么区别呢?在什么场景下应该使用哪一种排序规则呢?本文将对这两…

    database 2023年5月22日
    00
  • redis阻塞及解决方法

    目录 阻塞分析 客户端 磁盘 主从节点 切片集群 小结 解决方案 异步的子线程机制 分批读取 控制RBD大小 阻塞分析 客户端 复杂度高的增删改查操作1、集合全量查询和聚合操作2、bigkey 删除3、清空数据库 磁盘 1、AOF 日志同步写 主从节点 1、从库接收 RDB 文件后、清空数据库、加载 RDB 文件; 切片集群 向其他实例传输哈希槽信息,数据迁…

    Redis 2023年4月12日
    00
合作推广
合作推广
分享本页
返回顶部