Nacos 寻址机制

news2024/11/17 7:20:52

目录

1. 什么是寻址机制

2. 源码讲解

MemberLookup

AbstractMemberLookup

2.1 单机寻址

2.2. 文件寻址

2.3 地址服务器寻址


1. 什么是寻址机制

假设存在一个 Nacos 集群,其内部具有 A , B , C 三个节点。

 

客户端如何决定向集群中的哪个节点发送请求

在 application.yml 配置中,会列出 Nacos 集群的全部节点地址信息。

spring:
  cloud:
    nacos:
      discovery:
        username: nacos
        password: nacos
        server-addr: 127.0.0.1:8848,127.0.0.1:8850,127.0.0.1:8852

在最终实际发送请求时,会将 127.0.0.1:8848,127.0.0.1:8850,127.0.0.1:8852 会解析为 List<String> servers,然后随机选择一个节点发送请求。

public String reqApi(String api, Map<String, String> params, Map<String, String> body, List<String> servers,String method) throws NacosException {
    // 省略非关键代码
        
    Random random = new Random();
    
    // 随机选择一个节点
    int index = random.nextInt(servers.size());
    String server = servers.get(index);
              
    return callServer(api, params, body, server, method);
}

现有如下客户端请求

客户端 E 向 集群节点 A 发送一个注册服务的请求。

 

随后客户端 F 向 集群节点 B 发送一个服务列表查询请求。

 

此时整个系统有问题呀,明明已经有了注册服务,但是后面的请求反没有返回服务。

我们的目的就是让客户端能够拿到整个集群的数据。

第一种方式:增加访问前置路由

 

也就是说,客户端通过一个前置路由的角色将请求定位到正确的节点上然后返回数据。

这种方式很常用在数据分片上,即每个节点只存储整个集群的部分数据。

前置路由容易成为系统瓶颈。

第二种方式:每个节点存储全量数据

 

这样处理后,每个节点都可以独立完成工作,直接处理请求。

这就要求,当一个节点接受到注册请求后需要将信息同步到集群里的全部节点上。

 

那么客户端如何知道其他节点呢?这就是本节要探讨的主题:寻址机制。

寻址机制即 集群中的节点如何感知彼此

目前 Nacos 中,集群寻址有 文件 和 地址服务器 两种方式。而由于还存在单机模式,也相应的有个单机寻址方式。

单机寻址:自己就是节点

文件寻址:读取 cluster.conf 文件,获得节点信息

地址服务器寻址:请求地址服务器,通过接口返回节点信息

2. 源码讲解

MemberLookup

寻址机制的顶层接口为 com.alibaba.nacos.core.cluster.MemberLookup,代表一个寻址机制

public interface MemberLookup {
    // 省略非关键代码
    
    // 启动寻址
    void start() throws NacosException;
​
    
    // 找到节点后 调用该方法将节点传进来
    void afterLookup(Collection<Member> members);
    
    // 销毁寻址
    void destroy() throws NacosException;
    
}

AbstractMemberLookup

 

整个寻址类图如上。

AbstractMemberLookup 作为一个抽象类实现了 MemberLookup 接口,完成了一些通用功能供子类使用。

public abstract class AbstractMemberLookup implements MemberLookup {
    
    protected ServerMemberManager memberManager;
    
    protected AtomicBoolean start = new AtomicBoolean(false);
    
    @Override
    public void injectMemberManager(ServerMemberManager memberManager) {
        this.memberManager = memberManager;
    }
    
    @Override
    public void afterLookup(Collection<Member> members) {
        this.memberManager.memberChange(members);
    }
    
    @Override
    public void destroy() throws NacosException {
        if (start.compareAndSet(true, false)) {
            doDestroy();
        }
    }
    
    @Override
    public void start() throws NacosException {
        if (start.compareAndSet(false, true)) {
            doStart();
        }
    }
    
    // 供子类重写
    protected abstract void doStart() throws NacosException;
    
    // 供子类重写
    protected abstract void doDestroy() throws NacosException;
}

该类完成了start、destory 时 start 变量状态的维护,以及当子类找到集群节点时会调用 父类 AbstractMemberLookup 的afterLookup 方法,该方法通过调用 memberManager.memberChange(members); 完成集群节点变更。

2.1 单机寻址

单机模式,配置 JVM 虚拟机参数 -Dnacos.standalone=true 即可触发单机寻址。

源码

public class StandaloneMemberLookup extends AbstractMemberLookup {
    
    @Override
    public void doStart() {
        String url = EnvUtil.getLocalAddress();
        // 将自己封装为节点 Member 对象调用告知父类
        afterLookup(MemberUtil.readServerConf(Collections.singletonList(url)));
    }
    
    @Override
    protected void doDestroy() throws NacosException {
    
    }
    
    @Override
    public boolean useAddressServer() {
        return false;
    }
}

可以看出,单机寻址,就是找到自己就行了。

2.2. 文件寻址

文件寻址即通过 cluster.conf 文件来发现其他节点,cluster.conf 为 Nacos 的集群配置文件,文件内容就是其他集群中其他节点的地址信息

 

配置 JVM 虚拟机参数 -Dnacos.home=xxx,需要保证文件在 xxx/conf/cluster.conf

 

源码

public class FileConfigMemberLookup extends AbstractMemberLookup {
    
    private static final String DEFAULT_SEARCH_SEQ = "cluster.conf";
    
    // 文件变动监听器
    private FileWatcher watcher = new FileWatcher() {
        @Override
        public void onChange(FileChangeEvent event) {
            readClusterConfFromDisk();
        }
        
        @Override
        public boolean interest(String context) {
            return StringUtils.contains(context, DEFAULT_SEARCH_SEQ);
        }
    };
    
    @Override
    public void doStart() throws NacosException {
        readClusterConfFromDisk();
        // 注册文件监听器
        WatchFileCenter.registerWatcher(EnvUtil.getConfPath(), watcher);         
    }
    
 
    @Override
    protected void doDestroy() throws NacosException {
        // 取消注册
        WatchFileCenter.deregisterWatcher(EnvUtil.getConfPath(), watcher);
    }
    
    
    // 从磁盘中读取 cluster.conf 文件内容
    private void readClusterConfFromDisk() {
        Collection<Member> tmpMembers = new ArrayList<>();
 
        List<String> tmp = EnvUtil.readClusterConf();
        tmpMembers = MemberUtil.readServerConf(tmp);
 
        // 通知父类找到了节点信息
        afterLookup(tmpMembers);
    }
}

首先 Nacos 启动的时候会读取一次硬盘里 cluster.conf 文件内容。

然后向操作系统的 _inotify_ 机制注册一个目录监听器,对${NacosHome}/conf 下的文件监听变化情况,当产生变化了,会再次触发读取硬盘 cluster.conf 文件内容。

2.3 地址服务器寻址

当节点需要收缩扩容的时候,需要手动修改 cluster.conf 的 成员节点列表内容。集群节点少了还好,多了话就很费事费力,不易维护了。

地址服务器寻址就是 将节点信息存储在一个 web 服务器上,节点通过 HTTP 访问 Web 服务器 URL 获取节点信息。

每个节点定期向该web服务器请求cluster.conf的文件内容,来实现集群间的寻址、扩容、收缩。

服务器去哪找呢,我们自己能不能写个接口给用呢?

其实nacos已经实现了, nacos-address 模块。

 

其实 nacos-address 也是一个 nacos 节点,我们用单机模式启动一个address-server

 

我们先请求接口注册一个节点。

由于地址服务器需要授权登录,但是这里我暂时没搞懂,所以先做一个魔改。

 

这里改为 permitAll() 即直接放行。

然后注册节点。直接生成 HTTP 调用代码

 

POST http://localhost:8080/nacos/v1/as/nodes?product=life&cluster=default&ips=127.0.0.1

其中要填写产品名、集群名、还有注册的ip列表(逗号分隔)

这里我随便写了个,运行,然后注册成功。

 

接下来验证下获取节点,看能不能获取到。

 

这个接口要传两个路径参数,就是注册时填的 product 和 集群。然后请求发现了返回成功。

 

然后是以 地址服务器寻址方式启动 Nacos

节点启动配置项:(JVM 参数)

-Dnacos.home=D:\NacosClusterHome\NacosHome8848 // 配置 nacos home -Dnacos.core.member.lookup.type=address-server // 配置寻址方式为地址服务器寻址 -Daddress.server.domain=127.0.0.1 // 地址服务器域名 -Daddress.server.port=8080 // 地址服务器端口 -Daddress.server.url=/life/default // 服务列表接口

 

配置好后,启动节点,进入控制台,观察集群节点信息,发现可以看到刚才注册的节点信息。

 

源码

public class AddressServerMemberLookup extends AbstractMemberLookup {
    // 省略非关键代码
    
    // 同步任务延迟毫秒 5000 毫秒 = 5 秒
    private static final long DEFAULT_SYNC_TASK_DELAY_MS = 5_000L;
    
    @Override
    public void doStart() throws NacosException {
        run();
    }
 
  
    private void run() throws NacosException {
        // 从地址服务器URL同步数据
        syncFromAddressUrl();     
        
        // 创建定时任务,定期同步数据
        GlobalExecutor.scheduleByCommon(new AddressServerSyncTask(), DEFAULT_SYNC_TASK_DELAY_MS);
    }
     
    // 从地址服务器URL同步数据
    private void syncFromAddressUrl() {
        // 调用地址服务器接口
        RestResult<String> result = restTemplate
            .get(addressServerUrl, Header.EMPTY, Query.EMPTY, genericType.getType());
         
        // 读取接口返回值
        Reader reader = new StringReader(result.getData());
          
        // 通知父类找到节点信息了
        afterLookup(MemberUtil.readServerConf(EnvUtil.analyzeClusterConf(reader)));         
    }
    
    // 同步任务
    class AddressServerSyncTask implements Runnable {
        
        @Override
        public void run() {
            syncFromAddressUrl();
            
            // 同步完成后再创建下一次的定时同步任务
            GlobalExecutor.scheduleByCommon(this, DEFAULT_SYNC_TASK_DELAY_MS);
        }
    }
}

关键代码如上

本质还是通过调用接口获取节点信息,然后又创建了定时任务,定期同步数据,默认5秒。

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

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

相关文章

基于Springboot+Mybatis+mysql+element-vue高校就业管理系统

基于SpringbootMybatismysqlelement-vue高校就业管理系统一、系统介绍二、功能展示1.用户登陆注册2.个人信息(学生端)3.查看企业岗位信息&#xff08;学生端&#xff09;4.我的应聘(学生端)5.学生信息管理&#xff08;辅导员&#xff09;6.三方协议书审核&#xff08;辅导员&am…

向用户展示推荐算法,TikTok主动“透明化”

获悉&#xff0c;近日TikTok宣布推出一项新功能&#xff0c;用户可以查看推荐主页&#xff08;For You&#xff09;中某个视频被推荐的原因&#xff0c;这一功能让用户拥有了更多的知情权&#xff0c;让TikTok的算法变得更透明化。新功能新功能推出后&#xff0c;用户可以在视频…

Diskless:云与互联网数据中心的下一个大变革

作为数字经济的核心产业&#xff0c;云与互联网数据中心正面临着下一波技术浪潮的冲击&#xff1a;云原生应用和Serverless无服务器计算的全面普及&#xff0c;推动数据中心底层硬件资源彻底解耦池化和重组整合&#xff0c;形成新的扁平分层——新型存算分离硬件架构&#xff0…

定时循环执行Python脚本 —— 定时执行专家

目录 提前准备 方案一、执行DOS命令 方式 1、在《定时执行专家》里新建“执行DOS命令”任务 方案二、执行脚本文件 方式 1、编写 .bat 脚本&#xff0c;用来执行Python脚本 2、在《定时执行专家》里新建“执行脚本文件”任务 本文提供两种使用《定时执行专家》定时循环执…

挑选在线客服系统的七大注意事项

在线客服系统是客户关怀软件&#xff0c;能够为访客和客服提供即时对话&#xff0c;对访客来说&#xff0c;能够为其提供一个快速、高效的沟通方式&#xff0c;即时性的互动提高访客沟通体验&#xff1b;对客服来说&#xff0c;有效提高客服效率,为客服人员节省大量宝贵时间。 …

机器学习 | 支持向量机

一.基本原理 SVM是一种二分类模型 基本思想&#xff1a;在特征空间中寻找间隔最大的分离超平面使数据得到高效的二分类&#xff0c;具体来讲&#xff0c;有三种情况&#xff08;不加核函数的话就是线性模型&#xff0c;加了之后才会升级为一个非线性模型&#xff09; 当训练…

玩以太坊链上项目的必备技能(Constant 和 Immutable 状态变量-Solidity之旅十六)

constant&#xff08;常量&#xff09; 说到常量这一概念&#xff0c;拥有现代编程经历的您&#xff0c;对这一词再熟悉不过了。 常量&#xff0c;常量&#xff0c;顾名思义便是变量值始终不变&#xff0c;这在很多面向对象程序语言中都有。 相对 Solidity 来说&#xff0c;…

Web入门开发【六】- 域名认证

欢迎来到霍大侠的小院&#xff0c;我们来学习Web入门开发的系列课程。 首先我们来了解下这个课程能学到什么&#xff1f; 1、你将可以掌握Web网站的开发全过程。 2、了解基础的HTML&#xff0c;CSS&#xff0c;JavaScript语言。 3、开发自己的第一个网站。 4、认识很多对编…

LabVIEW如何减少下一代测试系统中的硬件过时2

LabVIEW如何减少下一代测试系统中的硬件过时2 HAL最佳实践 从通用测试函数中分离测试逻辑 一个重要的最佳实践是将特定于dut的测试逻辑与更通用的、可重用的测试模块分离&#xff0c;以提高重用性并减少重新验证和文档成本。解耦和使层模块化可以改进系统架构&#xff0c;使…

08 面向对象三大特性

目录 一 封装 1.1 概述 1.2 为什么要进行封装&#xff1f; 1.3 Java中的封装 1.4 四种访问权限修饰符 1.5 练习 二 继承 2.1 继承的由来 2.2 继承的好处 2.3 语法格式 2.4 继承的特点之一&#xff1a;成员变量 2.4.1 父类成员变量私有化 2.4.2 父类和子类成员变量…

二维刚体变换

欢迎访问我的博客首页。 二维刚体变换1. 二维旋转矩阵2. 参考1. 二维旋转矩阵 二维世界坐标系中任一点 P(x,y)P(x, y)P(x,y) 绕原点逆时针旋转 θ\thetaθ 度到点 P′(x′,y′)P(x, y)P′(x′,y′)&#xff0c;这个旋转可以用一个二维矩阵表示 R逆[cosθ−sinθsinθcosθ].(1.…

女文科生转行做程序员,工资涨了4倍,戏说“我是女生”怕啥秃顶

前几天&#xff0c;有一个女生在后台留言&#xff0c;问她要不要毕业后做程序员工作&#xff1f;自己特别纠结。 情况是这样的&#xff1a;她今年大学毕业&#xff0c;学计算机专业&#xff0c;有一定兴趣&#xff0c;但父母比较反对&#xff0c;列举了很多程序员熬夜加班的痛…

Fragment案例

Fragment案例 1.案例要求 框架布局项目难点&#xff1a;1 导航栏的实现&#xff0c;显示导航按钮、切换Fragment 2 每个Fragment的创建、显示 3 Fragment的跳转&#xff08;从新闻列表到新闻详情&#xff0c;再返回&#xff09; 涉及的技术&#xff1a;用RadioGroup及RadioButt…

【学习笔记07】vue3移动端的适配

目录1、创建一个项目并启动2、设置根字体大小和单位转化3、去掉边框距离4、css的嵌套使用5、连接到手机上显示6、vant ui 库的使用6.1 基础用法6.2 底部导航栏7、模拟锤子商城7.1 请求数据7.2 解决跨越7.3 组件切换7.4 轮播图的实现1、创建一个项目并启动 npm init vuelatestcd…

【OpenCV-Python】教程:7-4 KMeans 应用

OpenCV Python KMeans 应用 【目标】 使用 cv2.kmeans 对数据进行聚类 【代码】 1. 单个特征的 KMeans # 单特征数据的聚类 import numpy as np import cv2 from matplotlib import pyplot as pltx np.random.randint(25,100,25) y np.random.randint(175,255,25)z np.h…

Linux系统下管理员账号root忘记密码怎么找回

忘记root密码一般有两种情况&#xff1a; 一种是登上了root账号&#xff0c;但是忘记密码了&#xff0c;这种情况比较简单&#xff0c;在终端即可实现修改密码&#xff1b; 一种是登录不上root账号&#xff0c;这种情况稍微麻烦些&#xff0c;需要开机时进行一系列操作。 不能登…

【源码共读】Css-In-Js 的实现 classNames 库

classNames是一个简单的且实用的JavaScript应用程序&#xff0c;可以有条件的将多个类名组合在一起。它是一个非常有用的工具&#xff0c;可以用来动态的添加或者删除类名。 仓库地址&#xff1a;classNames 使用 根据classNames的README&#xff0c;可以发现库的作者对这个…

Spring 事务失效的常见八大场景,注意避坑

1. 抛出检查异常导致事务不能正确回滚 Servicepublic class Service1 {Autowiredprivate AccountMapper accountMapper;Transactionalpublic void transfer(int from, int to, int amount) throws FileNotFoundException {int fromBalance accountMapper.findBalanceBy(from);…

【源码共读】学习 axios 源码整体架构 (II)

源码分析 跳转至Axios.js文件中 // 构造函数 constructor(instanceConfig) {this.defaults instanceConfig// 创建对应的拦截器this.interceptors {request: new InterceptorManager(),response: new InterceptorManager()} } 那么&#xff0c;拦截器是怎么创建的呢 首先&a…

【云服务器 ECS 实战】一文掌握弹性伸缩服务原理及配置方法

1. 弹性伸缩概述2. 实现模式3. 基于 GRE 实现 VPC 的互联4. 弹性伸缩服务的配置使用4.1 创建伸缩组4.2 伸缩配置4.3 创建伸缩规则1. 弹性伸缩概述 弹性伸缩&#xff08;Auto Scaling&#xff09;就是自动为我们调整弹性计算资源大小&#xff0c;以满足业务需求的变化&#xff…