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

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 show processlist 查看当前连接

    show processlist和show full processlist processlist命令的输出结果显示了有哪些线程在运行,不仅可以查看当前所有的连接数,还可以查看当前的连接状态帮助识别出有问题的查询语句等。 如果是root帐号,能看到所有用户的当前连接。如果是其他普通帐号,则只能看到自己占用的连接。showprocesslist只能列出当前1…

    MySQL 2023年4月13日
    00
  • MongoDB 监控工具mongostat和mongotop的使用

    MongoDB是一个高性能、分布式、面向文档的NoSQL数据库,使用它可以方便地存储和查询海量数据。但是在进行大规模的数据分析、复杂的查询及数据调优时,我们需要能够对MongoDB进行监控,进而优化系统性能。mongostat和mongotop是MongoDB监控工具中比较重要的两个,下面我将详细讲解它们的使用。 mongostat的使用 mongostat…

    database 2023年5月22日
    00
  • MongoDB更新文档方法详解

    MongoDB是一种流行的NoSQL数据库,提供了一个灵活的文档数据模型,使得更新文档相对来说很简单。在本文中,我们将学习在MongoDB中如何更新文档。 更新整个文档 更新整个文档就是把旧的文档替换成一个新的文档。下面是一个使用MongoDB shell语法更新整个文档的例子: db.inventory.updateOne( { item: "a…

    MongoDB 2023年3月14日
    00
  • redis连接报错error:NOAUTH Authentication required

    当我们在使用Redis时,可能会遇到“redis连接报错error:NOAUTH Authentication required”的错误提示,这是因为我们没有进行Redis的身份验证而导致连接失败。下面我将为大家介绍几种解决这个问题的方法。 方法一:在配置文件中设置密码 我们可以在Redis的配置文件中设置requirepass参数来为Redis设置密码。打…

    database 2023年5月22日
    00
  • Docker安装部署Redis数据库的实现步骤

    下面我将详细讲解基于Docker安装部署Redis数据库的实现步骤。 1. 准备工作 在开始安装和部署Redis之前,我们需要先在电脑上安装好Docker和Docker-Compose,具体安装方法可以参考官方文档进行操作。 2. 使用Docker-Compose配置Redis部署环境 Docker-Compose是一种定义和运行多容器Docker应用程序的…

    database 2023年5月22日
    00
  • 从零开始学习Node.js系列教程四:多页面实现数学运算的client端和server端示例

    针对这个题目,我将分成以下几个部分进行讲解: 文章介绍 环境搭建 代码编写 示例说明 文章介绍 本文是从零开始学习Node.js系列教程的第四部分,主要探讨如何在client端和server端实现数学运算。本文的主要内容包括: 如何搭建client-server基本架构 如何实现多路径的路由 如何在client端和server端实现数学运算 环境搭建 在进行…

    database 2023年5月21日
    00
  • SQLserver2008使用表达式递归查询

    下面是“SQL server 2008使用表达式递归查询”的完整攻略。 什么是表达式递归查询 表达式递归查询是一种使用递归方式查询数据的方法。它与常规递归的不同之处在于它使用了SQL Server的WITH语句,这使得它更容易理解而且性能更好。在这种类型的查询中,一个查询使用自身的输出来生成下一个查询的输入,这样就可以逐步构造出一个结果集。 使用表达式递归查…

    database 2023年5月21日
    00
  • 详解MySQL Shell 运行 SQL 的两种内置方法

    详解MySQL Shell 运行 SQL 的两种内置方法 MySQL Shell是MySQL官方的新一代命令行客户端,它支持运行SQL,并且提供了两种内置的方法来运行SQL。本文将详细介绍这两种方法以及它们的使用方式。 方法一:使用 sql() 函数 sql(sql_statement) sql() 函数可以接收一个 SQL 语句作为参数,并且会返回执行结果…

    database 2023年5月22日
    00
合作推广
合作推广
分享本页
返回顶部