基于 Zookeeper 实现服务注册和服务发现

这篇具有很好参考价值的文章主要介绍了基于 Zookeeper 实现服务注册和服务发现。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

无论是采用SOA还是微服务架构,都需要使用服务注册和服务发现组件。我刚开始接触 Dubbo 时一直对服务注册/发现以及 Zookeeper 的作用感到困惑,现在看来是因为对分布式系统的理解不够深入,对 Dubbo 和 Zookeeper 的工作原理不够清楚。

本文将基于 Zookeeper 实现服务注册和服务发现功能,如果跟我一样有同样的困惑,希望可以通过本文了解其他组件如何使用 Zookeeper 作为注册中心的工作原理。

声明

文章中所提供的代码仅供参考,旨在帮助缺乏基础知识的开发人员更好地理解服务注册和服务发现的概念。请注意,这些代码并不适用于实际应用中

前置知识

服务注册和发现

在SOA或微服务架构中,由于存在大量的服务以及可能的相互调用,为了更有效地管理这些服务,我们通常需要引入一个统一的地方,即注册中心,来集中管理它们,而注册中心最基本的功能就是服务注册/发现。

  • 服务注册:将该服务实例的元数据(如IP地址、端口号、健康状态等)注册到注册中心,这样其他服务或客户端可以发现和使用该服务。
  • 服务发现:当一个服务需要调用别的服务时,使用静态配置是不可行的,这个时候可以去注册中心获取可用的服务实例并调用。

Zookeeper

Zookeeper 是一个传统的分布式协调服务,它更多的被用来作为一个协调器使用,比如来协调管理 Hadoop 集群、协调 Kafka 的 leader 选举等。

为什么会有组件将其视为一个注册中心使用?我想有几个原因:

  1. Zookeeper 在分布式系统中具有更强的一致性和可靠性,可以确保各个服务的注册信息保持一致。
  2. Zookeeper 使用内存存储数据,具有很高的读写性能。这对于注册中心来说非常关键,因为它需要快速地响应客户端的请求。
  3. Zookeeper 的 Watcher 机制可以让客户端监听指定节点的变化。当某个节点(注册中心)发生变化时,Zookeeper 可以通知其他服务实现实时更新。

工作原理

以下图为例,可以看到 Dubbo 是如何使用 Zookeeper 实现服务注册/发现的。

基于 Zookeeper 实现服务注册和服务发现,中间件,Java,# 微服务,java,注册中心,zookeeper

  1. 服务提供者向 /dubbo/com.foo.BarService/providers 目录下写入自己的 URL 地址。
  2. 服务消费者订阅 /dubbo/com.foo.BarService/providers 目录下的提供者 URL 地址。并向 /dubbo/com.foo.BarService/consumers 目录下写入自己的 URL 地址。

这里的目录就是 Zookeeper 的数据结构,原理非常简单,本质上就是服务提供者和消费者按照约定在 Zookeeper 上读写数据,同时借用其 Watcher 机制、临时节点和可靠性等特性高效的实现以下功能:

  • 当提供者服务出现断电等异常停机时,注册中心能自动删除提供者信息。
  • 当注册中心重启时,能自动恢复注册数据以及订阅请求。

实现过程

注册中心

下面通过 Zookeeper 的 Java API 实现一个只包含服务注册/发现的注册中心,代码如下:

public class RegistrationCenter {

    // 连接信息
    private String connectString = "192.168.10.11:2181,192.168.10.11:2182,192.168.10.11:2183";

    // 超时时间
    private int sessionTimeOut = 30000;

    private final String ROOT_PATH = "/servers";

    private ZooKeeper client;

    public RegistrationCenter() {
        this(null);
    }

    public RegistrationCenter(Consumer<List<String>> consumer) {
        try {
            getConnection(null == consumer ? null : watchedEvent -> {
                //监听服务器地址的上下线
                if (watchedEvent.getType() == Watcher.Event.EventType.NodeChildrenChanged) {
                    try {
                        consumer.accept(subServers());
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });
            Stat stat = client.exists(ROOT_PATH, false);
            if (stat == null) {
                //创建根节点
                client.create(ROOT_PATH, ROOT_PATH.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * @param serverName 将服务器注册到zk集群时,所需的服务名称
     * @param metadata   服务元数据
     * @throws Exception
     */
    public void doRegister(String serverName, Metadata metadata) throws Exception {


        /**
         * ZooDefs.Ids.OPEN_ACL_UNSAFE: 此权限表示允许所有人访问该节点(服务器)
         * CreateMode.EPHEMERAL_SEQUENTIAL: 由于服务器是动态上下线的,上线后存在,下线后不存在,所以是临时节点
         * 而服务器一般都是有序号的,所以是临时、有序的节点.
         */
        String node = client.create(ROOT_PATH + "/" + serverName, metadata.toString().getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);

        System.out.println(serverName + " 已经上线");
    }


    /**
     * 发现/订阅服务
     */
    public List<String> subServers() throws InterruptedException, KeeperException {
        List<String> zkChildren = client.getChildren(ROOT_PATH, true);
        List<String> servers = new ArrayList<>();
        zkChildren.forEach(node -> {
            //拼接服务完整信息
            try {
                byte[] data = client.getData(ROOT_PATH + "/" + node, false, null);
                servers.add(new String(data));
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
        return servers;
    }

    private void getConnection(Watcher watcher) throws IOException {
        this.client = new ZooKeeper(connectString, sessionTimeOut, watcher);
    }

    /**
     * 服务元数据
     */
    public static class Metadata {
        public Metadata() {
        }

        public Metadata(String ip, int port) {
            this.ip = ip;
            this.port = port;
        }

        private String ip;

        private int port;

        public String getIp() {
            return ip;
        }

        public void setIp(String ip) {
            this.ip = ip;
        }

        public int getPort() {
            return port;
        }

        public void setPort(int port) {
            this.port = port;
        }

        @Override
        public String toString() {
            return "{" + "ip='" + ip + '\'' + ", port=" + port + '}';
        }
    }
}

该类中两个核心方法:doRegister()subServers() 服务注册和订阅。

  • doRegister()主要是往 Zookeeper 中创建了一个临时节点数据,临时节点的优势就是当服务出现断电等异常停机时,节点会自动删除。
  • subServers()则是去 Zookeeper 读取了所有服务提供者的信息,并且监听了节点状态,当节点发生创建、删除、更新等事件时重新获取服务者的信息,做到数据实时更新。

至此,一个简单的注册中心就完成了,当然,如果要实现一个成熟的注册中心,还要考虑负载均衡、高可用性和容错、服务治理和路由控制等功能,这里先不展开。

服务注册

当有了注册中心,服务提供者就可以调用 doRegister() 进行注册,代码如下:

public class ProviderServer {
    public static void main(String[] args) throws Exception {
        RegistrationCenter registrationCenter = new RegistrationCenter();
        registrationCenter.doRegister("provider", new RegistrationCenter.Metadata("127.0.0.1", 8080));
        Thread.sleep(Long.MAX_VALUE);
    }
}

服务发现

同样,服务消费者可以调用 subServers() 发现服务提供者,同时当服务提供者发生变化时会通知到消费者。代码如下:

public class ConsumerServer {
    public static void main(String[] args) throws Exception {
        RegistrationCenter registrationCenter = new RegistrationCenter(newServers -> {
            System.out.println("服务更新了..."+newServers);
        });
        List<String> servers = registrationCenter.subServers();
        System.out.println(servers);
        Thread.sleep(Long.MAX_VALUE);
    }
}

总结

服务注册和服务发现功能是为了解决分布式系统中的服务管理和通信问题而设计的,经过不断的发展与负载均衡、健康监测、服务治理和路由控制等功能完善成为一个注册中心。服务注册和服务发现有助于实现系统的弹性和可扩展性,因为新的服务实例可以动态地加入系统,而无需手动配置和修改已有的代码。文章来源地址https://www.toymoban.com/news/detail-695232.html

到了这里,关于基于 Zookeeper 实现服务注册和服务发现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 微服务-服务的注册与发现(Consul、zookeeper、etcd、eureka、Nacos)

    一. 对比常用的注册中心 Consul、zookeeper、etcd、eureka、Nacos Feature Consul Zookeeper Etcd Eureka Nacos 服务健康检查  服务状态,内存,硬盘等  (弱)长连接,keepalive  连接心跳  可配支持 传输层 (PING 或 TCP)和应用层 (如 HTTP、MySQL、用户自定义)的健康检查 多数据中心  支持  —  

    2023年04月19日
    浏览(40)
  • 服务注册与发现Eureka、Zookeeper、Consul 三个注册中心的异同点(CAP理论)

    Eureka Eureka是由Netflix开源的一个服务注册和发现组件,它主要用于构建高可用、分布式系统的基础设施中。Eureka的服务器端被称为Eureka Server,客户端则是那些需要注册的服务。Eureka具有以下特点: 高可用性 :Eureka支持多节点部署,能够在节点故障时自动进行故障转移。 自我

    2024年04月22日
    浏览(25)
  • Zookeeper 实战 | Zookeeper 和Spring Cloud相结合解决分布式锁、服务注册与发现、配置管理

    专栏集锦,大佬们可以收藏以备不时之需: Spring Cloud 专栏: Python 专栏: Redis 专栏: TensorFlow 专栏: Logback 专栏: 量子计算: 量子计算 | 解密著名量子算法Shor算法和Grover算法 AI机器学习实战: AI机器学习实战 | 使用 Python 和 scikit-learn 库进行情感分析 AI机器学习 | 基于lib

    2024年02月05日
    浏览(49)
  • SpringCloud学习笔记(上):服务注册与发现:Eureka、Zookeeper、Consul+负载均衡服务调用:Ribbon

    SpringCloud=分布式微服务架构的一站式解决方案,是多种微服务架构落地技术的集合体,俗称微服务全家桶。 springboot版本选择: git源码地址:https://github.com/spring-projects/spring-boot/releases/ SpringBoot2.0新特性:https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0-Release springcloud版本选

    2024年02月08日
    浏览(32)
  • 【分布式技术专题】「Zookeeper中间件」zookeeper的服务器的log4j升级为log4j2的升级方案(忽略配置化兼容问题)

    目前希望可以升级将Zookeeper中log4j的版本升级到log4j2版本,并且要避开相关的log4j2的安全隐患问题,此时需要考虑的就是针对于如何将无缝衔接log4j2的版本jar包的安装呢?我们接下来观察一下看看问题所在。目前我采用的环境是windows环境,不过也同样对其他操作系统有效,毕

    2024年02月20日
    浏览(33)
  • 中间件 -zookeeper

    三连支持 一起鼓励 一起进步 zookeeper 首先,在分布式架构中,任何的节点都不能以单点的方式存在,因此我们需要解决单点的问 题。常见的解决单点问题的方式就是集群 大家来思考一下,这个集群需要满足那些功能? 集群中要有主节点和从节点(也就是集群要有角色) 集

    2024年02月10日
    浏览(27)
  • 中间件上云部署 zookeeper

    storageclass ingress 注意: Pod Disruption Budget (pod 中断 预算),含义其实是 终止pod前 通过labelSelector机制获取正常运行的pod数目的限制,目的是对主动驱逐的保护措施。 场景 节点维护或升级时(kubectl drain) 对应用的自动缩容操作(autoscaling down) 由于节点不可用(not ready)导致的Pod驱逐就

    2024年02月17日
    浏览(33)
  • 基于Promise.resolve实现Koa请求队列中间件

    本文作者为360奇舞团前端工程师 最近在做一个 AIGC 项目,后端基于 Koa2 实现。其中有一个需求就是调用兄弟业务线服务端 AIGC 能力生成图片。但由于目前兄弟业务线的 AIGC 项目也是处于测试阶段,能够提供的服务器资源有限,当并发请求资源无法满足时,会响应【服务器繁忙

    2024年02月13日
    浏览(26)
  • 【云原生进阶之PaaS中间件】第二章Zookeeper-1-综述

            ZooKeeper 是一个分布式的,开放源码的分布式应用程序协调服务,它包含一个简单的原语集,分布式应用程序可以基于它实现同步服务,配置维护和命名服务等。 Zookeeper是hadoop的一个子项目,其发展历程无需赘述。在分布式应用中,由于工程师不能很好地使用锁机

    2024年02月09日
    浏览(34)
  • Go重写Redis中间件 - GO实现TCP服务器

    首先新建一个项目go-redis,将config和lib包放到项目中,config.go用来解析配置,比如端口、功能、DB数;lib包有两个文件夹,分别是logger和sync,其中logger.go是一个日志框架,sync包中的bool.go包装了atomic操作,因为atomic原生没有bool类型,所以将uint32类型改造成bool型的atomic,wait.g

    2024年02月15日
    浏览(65)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包