基于 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 实现服务注册/发现的。

在这里插入图片描述

  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;}@Overridepublic 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);}
}

总结

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/66740.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

React 18 对 state 进行保留和重置

参考文章 对 state 进行保留和重置 各个组件的 state 是各自独立的。根据组件在 UI 树中的位置&#xff0c;React 可以跟踪哪些 state 属于哪个组件。可以控制在重新渲染过程中何时对 state 进行保留和重置。 UI 树 浏览器使用许多树形结构来为 UI 建立模型。DOM 用于表示 …

解决Linux Ubuntu上安装RabbitMQ服务后的公网远程访问问题,借助cpolar内网穿透技术

文章目录 前言1.安装erlang 语言2.安装rabbitMQ3. 内网穿透3.1 安装cpolar内网穿透(支持一键自动安装脚本)3.2 创建HTTP隧道 4. 公网远程连接5.固定公网TCP地址5.1 保留一个固定的公网TCP端口地址5.2 配置固定公网TCP端口地址 前言 RabbitMQ是一个在 AMQP(高级消息队列协议)基…

软件产品确认测试鉴定测试

软件产品确认测试 确认测试也称鉴定测试&#xff0c;即验证软件的功能、性能及其它特性是否与用户的要求一致。软件确认测试是在模拟的环境下&#xff0c;验证软件是否满足需求规格说明书列出的需求。为此&#xff0c;需要首先制定测试计划&#xff0c;规定要做测试的种类&…

Pinely Round 2 (Div. 1 + Div. 2) F. Divide, XOR, and Conquer(区间dp)

题目 给定长为n(n<1e4)的数组&#xff0c;第i个数为ai(0<ai<2的60次方) 初始时&#xff0c;区间为[1,n]&#xff0c;也即l1&#xff0c;rn&#xff0c; 你可以在[l,r)中指定一个k&#xff0c;将区间分成左半边[l,k]、右半边[k1,r] 1. 如果左半边异或和与异或和的异…

分类预测 | MATLAB实现GRNN广义回归神经网络多特征分类预测

分类预测 | MATLAB实现GRNN广义回归神经网络多特征分类预测 目录 分类预测 | MATLAB实现GRNN广义回归神经网络多特征分类预测分类效果基本介绍模型描述预测过程程序设计参考资料分类效果 基本介绍 MATLAB实现GRNN广义回归神经网络多特

自建音乐服务器Navidrome之一

这里写自定义目录标题 1.1 官方网站 2. Navidrome 简介2.1 简介2.2 特性 3. 准备工作4. 视频教程5. 界面演示5.1 初始化页5.2 专辑页 前言 之前给大家介绍过 Koel 音频流服务&#xff0c;就是为了解决大家的这个问题&#xff1a;下载下来的音乐&#xff0c;只能在本机欣赏&…

「Redis」1. 数据类型的底层实现

前言&#xff1a;在这篇博文中&#xff0c;我们将简单总结在面试中怎么回答Redis数据类型的底层实现。 因为面试时间就那么点&#xff0c;言简意赅的描述自己会的知识显得尤为重要‼️ 文章目录 0.1. String 的底层实现原理0.2. 列表的底层实现原理0.3. 字典的底层实现原理0.4.…

​放弃数据库,改用Kafka!

长期以来&#xff0c;数据库一直充当着记录系统&#xff0c;它们以可靠且持久的方式存储和管理关键数据&#xff0c;也赢得了大多数公司的信赖。 但时代在变。许多新兴趋势正在影响当今数据的存储和管理方式&#xff0c;不得不让一些技术决策者们重新考虑数据存储究竟还有哪些…

敏感接口权限校验

前端校验 &#xff08;从前端或者从token里面拿一下&#xff09;&#xff0c;看一下用户有没有这个页面的权限&#xff08;但是一般不用&#xff0c;因为nodejs也可以写后端&#xff0c;但是放到前端去校验不安全&#xff09; 后端校验 需要梳理敏感数据接口&#xff0c;将这…

重写 UGUI

重写Button using UnityEngine; using UnityEngine.UI; public class MyButton : Button {[SerializeField] private int _newNumber; }using UnityEditor;//编辑器类在UnityEditor命名空间下。所以当使用C#脚本时&#xff0c;你需要在脚本前面加上 "using UnityEditor&q…

Hive-安装与配置(1)

&#x1f947;&#x1f947;【大数据学习记录篇】-持续更新中~&#x1f947;&#x1f947; 个人主页&#xff1a;beixi 本文章收录于专栏&#xff08;点击传送&#xff09;&#xff1a;【大数据学习】 &#x1f493;&#x1f493;持续更新中&#xff0c;感谢各位前辈朋友们支持…

微信小程序修改vant组件样式

1 背景 在使用vant组件开发微信小程序的时候&#xff0c;想更改vant组件内部样式&#xff0c;达到自己想要的目的&#xff08;van-grid组件改成宫格背景色为透明&#xff0c;默认为白色&#xff09;&#xff0c;官网没有示例&#xff0c;通过以下几步修改成功。 2 步骤 2.1 …

随机森林算法

介绍 随机森林是一种基于集成学习的有监督机器学习算法。随机森林是包含多个决策树的分类器&#xff0c;一般输出的类别是由决策树的众数决定。随机森林也可以用于常见的回归拟合。随机森林主要是运用了两种思想。具体如下所示。 Breimans的Bootstrap aggregatingHo的random …

重装系统全流程

重点&#xff1a; 下载镜像网址&#xff1a;下载 Windows 10 (microsoft.com) 不过不用下载&#xff0c;你的美均相U盘里面有下载好的Win10系统 重点注意&#xff0c;重启后拔优盘&#xff0c;安装时不要联网

PID串行多闭环控制与并行多闭环控制的优缺点分析和应用比较

导言&#xff1a; 在自动控制领域&#xff0c;PID控制器是一种经典的控制策略&#xff0c;被广泛应用于各种工业和非工业过程。随着控制系统的复杂性增加&#xff0c;PID串行多闭环控制和PID并行多闭环控制成为解决复杂控制问题的重要方法。本文将从优点和缺点的角度对这两种控…

Web服务器简介及HTTP协议

一、Web Server&#xff08;网页服务器&#xff09; 一个 Web Server 就是一个服务器软件&#xff08;程序&#xff09;&#xff0c;或者是运行这个服务器软件的硬件&#xff08;计算机&#xff09;。其主要功能是通过 HTTP 协议与客户端&#xff08;通常是浏览器&#xff08…

(二十)大数据实战——Flume数据采集的基本案例实战

前言 本节内容我们主要介绍几个Flume数据采集的基本案例&#xff0c;包括监控端口数据、实时监控单个追加文件、实时监控目录下多个新文件、实时监控目录下的多个追加文件等案例。完成flume数据监控的基本使用。 正文 监控端口数据 ①需求说明 - 使用 Flume 监听一个端口&am…

设计模式系列-创建者模式

一、上篇回顾 上篇我们主要讲述了抽象工厂模式和工厂模式。并且分析了该模式的应用场景和一些优缺点&#xff0c;并且给出了一些实现的思路和方案,我们现在来回顾一下&#xff1a; 抽象工厂模式&#xff1a;一个工厂负责所有类型对象的创建&#xff0c;支持无缝的新增新的类型对…

kotlin 转 Java

今天突然想研究下有些kotlin文件转为Java到底长什么样&#xff0c;好方便优化kotlin代码&#xff0c;搞了半天发现一个非常简单的Android Studio或者Intellij idea官方插件Kotlin&#xff0c;Kotlin是插件的名字&#xff0c;真是醉了&#xff1b; 这里以AS为例&#xff0c;使用…

pnpm快速创建 Vue.js 项目(npm类似)

目录 pnpm 创建一个 Vue.js 项目 前提准备&#xff1a; 运行创建命令&#xff1a; 选择项目配置&#xff1a;&#xff08;按需选择&#xff09; cd 项目名&#xff1a;&#xff08;进入项目终端&#xff09; 安装项目依赖&#xff1a; 运行项目&#xff1a; pnpm 创建一…