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日

相关文章

  • mybatis报错元素内容必须由格式正确的字符数据或标记组成异常的解决办法

    当我们使用mybatis时,有时会出现“元素内容必须由格式正确的字符数据或标记组成”这样的异常,这是由于我们的Mapper.xml或者配置文件中出现了不规范的语法导致的。 下面是解决这个异常的完整攻略: 1. 检查Mapper.xml文件是否正确 首先,我们需要检查Mapper.xml文件是否书写正确,并且所有的元素和属性是否符合标准的XML语法规范,如: …

    database 2023年5月21日
    00
  • mssql server .ldf和.mdf的文件附加数据库的sql语句

    当你想要在 MSSQL Server 中使用一个已有的数据文件时,你需要执行附加数据库的 SQL 语句,这个操作需要使用到 .ldf 和 .mdf 文件。下面是详细的步骤和示例说明。 步骤一:备份原数据库(可选) 由于附加数据库会删除数据文件之前数据库的副本,因此在附加数据库之前可选的步骤是备份原数据库,以便出现问题时可以恢复数据。 步骤二:附加数据文件 打…

    database 2023年5月21日
    00
  • redis set操作

    set 操作添加 sadd myset 1 2 3 4 5 6 7 8 9 0 1取数据 smembers myset127.0.0.1:6379> sadd myset 1 2 3 4 5 6 7 8 9 0 1(integer) 10127.0.0.1:6379> smembers myset 1) “0” 2) “1” 3) “2” 4) …

    Redis 2023年4月11日
    00
  • 如何选择MySQL存储引擎?

    MySQL是一个开源的关系型数据库管理系统,拥有多种存储引擎(Storage Engine),不同的引擎具有不同的特点和性能表现。可通过修改数据表的存储引擎来优化数据库的性能。 存储引擎介绍 MySQL支持的存储引擎较多,具体如下: 1 InnoDB:支持ACID事务,并发性高,行级锁定、MVCC、自适应哈希索引、为外键提供支持。 2 MyISAM:不支持事…

    MySQL 2023年3月9日
    00
  • C#实现Access通用访问类OleDbHelper完整实例

    为方便使用和操作Access数据库,我们可以开发一个通用访问类,可以实现对Access的封装和统一管理。本文将详细讲解C#实现Access通用访问类OleDbHelper完整实例的攻略。 介绍 OleDb是一种Microsoft发布的一种访问不同数据源的统一接口,并为不同应用程序提供统一的方式访问数据库。OleDb由系统提供,是系统自带支持的。在访问Acce…

    database 2023年5月21日
    00
  • 如何在Python中查询MongoDB数据库中的数据?

    以下是在Python中查询MongoDB数据库中的数据的完整使用攻略。 使用MongoDB数据库的前提条件 在使用Python连接MongoDB数据库之前,确保已经了MongoDB数据库,并已经创建使用数据库和集合,同时需要安Python的驱动程序,例如pymongo。 步骤1:导模 在Python中使用pymongo模块连接MongoDB数据库。以下是导入…

    python 2023年5月12日
    00
  • linux 下MySQL服务器的启动与停止

    下面是Linux下MySQL服务器的启动与停止攻略: 启动MySQL服务器 打开终端,输入以下命令登录MySQL: mysql -u用户名 -p密码 其中,用户名和密码需要替换为你自己设定的登录信息。如果输入成功,会显示以下信息: Welcome to the MySQL monitor. Commands end with ; or \g. Your My…

    database 2023年5月22日
    00
  • 详解Redis服务器的5种命令使用方法

    Redis是一款高性能的键值存储数据库,它提供了多种数据结构的支持,包括字符串、列表、哈希、集合和有序集合。这些数据结构可以非常方便地用于构建各种应用程序。 本文将详细介绍Redis服务器命令,包括基本命令、字符串命令、列表命令、哈希命令、集合命令和有序集合命令。 基本命令 以下是Redis服务器的基本命令: PING PING 该命令用于检查Redis服务…

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