nacos源码分析==客户端从服务端读取配置文件-服务端服务注册

news2024/11/15 11:35:41

客户端从服务端读取配置文件

客户端启动的时候会扫描到boostrap.yml中的信息,扫描到标签@ConditionalOnProperty会将NacosConfigBootstrapConfiguration 中的bean注入。其中NacosConfigProperties就是读取的boostrap.yml中spring.cloud.nacos.config下的配置项。NacosConfigBootstrapConfiguration创建除了NacosPropertySourceLocator对象并注入容器。

@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(name = "spring.cloud.nacos.config.enabled", matchIfMissing = true)
public class NacosConfigBootstrapConfiguration {

	@Bean
	@ConditionalOnMissingBean
//扫描bootstrap.yml中的spring.cloud.nacos.config下的配置项
	public NacosConfigProperties nacosConfigProperties() {
		return new NacosConfigProperties();
	}

	@Bean
	@ConditionalOnMissingBean
//得到NacosConfigService
	public NacosConfigManager nacosConfigManager(
			NacosConfigProperties nacosConfigProperties) {
		return new NacosConfigManager(nacosConfigProperties);
	}

	@Bean
//创建处NacosPropertySourceLocator
	public NacosPropertySourceLocator nacosPropertySourceLocator(
			NacosConfigManager nacosConfigManager) {
		return new NacosPropertySourceLocator(nacosConfigManager);
	}

	/**
	 * Compatible with bootstrap way to start.
	 */
	@Bean
	@ConditionalOnMissingBean(search = SearchStrategy.CURRENT)
	@ConditionalOnNonDefaultBehavior
	public ConfigurationPropertiesRebinder smartConfigurationPropertiesRebinder(
			ConfigurationPropertiesBeans beans) {
		// If using default behavior, not use SmartConfigurationPropertiesRebinder.
		// Minimize te possibility of making mistakes.
		return new SmartConfigurationPropertiesRebinder(beans);
	}

}

客户端启动的时候会与服务端建立连接(其实是为了去服务端拉取配置文件,当然会同时把连接创建了),调用栈如下,可见会执行到之前注入的NacosPropertySourceLocator中。

start:278, RpcClient (com.alibaba.nacos.common.remote.client)

ensureRpcClient:885, ClientWorker$ConfigRpcTransportClient (com.alibaba.nacos.client.config.impl)

getOneRunningClient:1044, ClientWorker$ConfigRpcTransportClient (com.alibaba.nacos.client.config.impl)

queryConfig:940, ClientWorker$ConfigRpcTransportClient (com.alibaba.nacos.client.config.impl)

getServerConfig:397, ClientWorker (com.alibaba.nacos.client.config.impl)
getConfigInner:166, NacosConfigService (com.alibaba.nacos.client.config)
getConfig:94, NacosConfigService (com.alibaba.nacos.client.config)
loadNacosData:85, NacosPropertySourceBuilder (com.alibaba.cloud.nacos.client)
build:73, NacosPropertySourceBuilder (com.alibaba.cloud.nacos.client)
loadNacosPropertySource:199, NacosPropertySourceLocator (com.alibaba.cloud.nacos.client)
loadNacosDataIfPresent:186, NacosPropertySourceLocator (com.alibaba.cloud.nacos.client)
loadNacosConfiguration:158, NacosPropertySourceLocator (com.alibaba.cloud.nacos.client)
loadSharedConfiguration:116, NacosPropertySourceLocator (com.alibaba.cloud.nacos.client)
locate:101, NacosPropertySourceLocator (com.alibaba.cloud.nacos.client)
locateCollection:51, PropertySourceLocator (org.springframework.cloud.bootstrap.config)
locateCollection:47, PropertySourceLocator (org.springframework.cloud.bootstrap.config)

initialize:95, PropertySourceBootstrapConfiguration (org.springframework.cloud.bootstrap.config)

applyInitializers:604, SpringApplication (org.springframework.boot)

prepareContext:373, SpringApplication (org.springframework.boot)
run:306, SpringApplication (org.springframework.boot)
run:1303, SpringApplication (org.springframework.boot)
run:1292, SpringApplication (org.springframework.boot)
main:22, RuoYiSystemApplication (com.ruoyi.system)

起始点是PropertySourceBootstrapConfiguration, 这个类是由could-context注入容器,

又因为PropertySourceBootstrapConfiguration实现了ApplicationContextInitializer接口,所以会在springboot启动过程(refresh方法前)被调用到(prepareContext-applyInitializers)它的initialize方法,会根据bootstarp.yml中指定的dev去加载对应的配置文件。initialize方法会调用之前已经注入的NacosPropertySourceLocator,

 com.alibaba.cloud.nacos.client.NacosPropertySourceLocator#locate

该方法会一次加载三个配置文件

loadSharedConfiguration(composite);第一配置文件application-dev.yml 
loadExtConfiguration(composite);这里没配置就没有
loadApplicationConfiguration(composite, dataIdPrefix, nacosConfigProperties, env);第三配置文件ruoyi-system-dev.yml

加载的方法都差不多,发起请求去server端获取

com.alibaba.cloud.nacos.client.NacosPropertySourceBuilder#loadNacosData

com.alibaba.nacos.client.config.NacosConfigService#getConfigInner

com.alibaba.nacos.client.config.impl.ClientWorker#getServerConfig

com.alibaba.nacos.client.config.impl.ClientWorker.ConfigRpcTransportClient#queryConfig

queryConfig方法中会创建RpcClient并调用start方法创建与服务端的连接,已有就拿来用。

com.alibaba.nacos.client.config.impl.ClientWorker.ConfigRpcTransportClient#ensureRpcClient

                RpcClient rpcClient = RpcClientFactory
                        .createClient(uuid + "_config-" + taskId, getConnectionType(), newLabels);
                if (rpcClient.isWaitInitiated()) {
                    initRpcClientHandler(rpcClient);
                    rpcClient.setTenant(getTenant());
                    rpcClient.clientAbilities(initAbilities());
                    rpcClient.start();
                }

com.alibaba.nacos.common.remote.client.RpcClient#start

在start方法中,创建了一个线程池,提交了两个死循环的任务。

queryConfig方法中去请求服务端拿取配置文件:

ConfigQueryResponse response = (ConfigQueryResponse) requestProxy(rpcClient, request, readTimeouts);

com.alibaba.nacos.common.remote.client.grpc.GrpcConnection#request该方法会在请求body中加入一个type字段,实际就是request的类名,获取配置文件的话就是ConfigQueryRequest。

读取到配置文件之后将配置项设置到环境变量中,其他组件就能读取到了

com.alibaba.cloud.nacos.client.NacosPropertySourceLocator#addFirstPropertySource

org.springframework.core.env.CompositePropertySource#addFirstPropertySource

来自客户端的请求,访问到服务端的handler为

com.alibaba.nacos.config.server.remote.ConfigQueryRequestHandler#handle

这个方法的由来为nacos服务端启动时,注入了GrpcSdkServer,它实现了BaseRpcServer,BaseRpcServer中有个@PostConstruct标注的start方法

com.alibaba.nacos.core.remote.BaseRpcServer#start

com.alibaba.nacos.core.remote.grpc.BaseGrpcServer#startServer

    
    @Override
    public void startServer() throws Exception {
        final MutableHandlerRegistry handlerRegistry = new MutableHandlerRegistry();
        //指定处理请求的handler
        addServices(handlerRegistry, new GrpcConnectionInterceptor());
        
        server = ServerBuilder.forPort(getServicePort()).executor(getRpcExecutor())
                .maxInboundMessageSize(getMaxInboundMessageSize()).fallbackHandlerRegistry(handlerRegistry)
                .compressorRegistry(CompressorRegistry.getDefaultInstance())
                .decompressorRegistry(DecompressorRegistry.getDefaultInstance())
                .addTransportFilter(new AddressTransportFilter(connectionManager))
                .keepAliveTime(getKeepAliveTime(), TimeUnit.MILLISECONDS)
                .keepAliveTimeout(getKeepAliveTimeout(), TimeUnit.MILLISECONDS)
                .permitKeepAliveTime(getPermitKeepAliveTime(), TimeUnit.MILLISECONDS)
                .build();
        
        server.start();
    }

会创建一个io.grpc的Server,这个是个抽象接口,实现类为ServerImpl,ServerImpl中有个InternalServer,InternalServer因为引入了netty,所以实现类为NettyServer 。并且调用addServices方法指定处理请求的acceptor为GrpcRequestAcceptor

com.alibaba.nacos.core.remote.grpc.BaseGrpcServer#addServices

 com.alibaba.nacos.core.remote.grpc.GrpcRequestAcceptor#request

然后根据请求参数中带的type=ConfigQueryRequest,从服务端的持有的registryHandlers

Map<String, RequestHandler> registryHandlers = new HashMap<>();

中选出本次要用的ConfigQueryRequestHandler。

com.alibaba.nacos.core.remote.RequestHandler#handleRequest

com.alibaba.nacos.config.server.remote.ConfigQueryRequestHandler#handle

com.alibaba.nacos.config.server.remote.ConfigQueryRequestHandler#getContext

看来是提前将配置文件缓存到了服务端本地而没有放在数据库里。

 

=======================================================

断点设置

从上可以看到,从客户端发到服务端的消息,肯定要经过客户端的com.alibaba.nacos.common.remote.client.grpc.GrpcUtils#convert(com.alibaba.nacos.api.remote.request.Request)

所以只要在这打个断点,就知道发往服务端的是什么,根据type参数也知道同时服务端应该用哪个handler来处理。 后面发现服务注册竟然没走这个接口,她在自己的方法里new了个请求对象,估计不是同一个人写的,该统一转换吧。。。

到达服务端的请求肯定要经过服务端的com/alibaba/nacos/core/remote/grpc/GrpcRequestAcceptor.java:96

服务注册走了这

从这能看到服务端谁来处理。大概都是type+Handler。

=================

服务端服务注册

客户端发送注册请求

com.alibaba.nacos.client.naming.remote.gprc.NamingGrpcClientProxy#requestToServer

 

服务端收到注册请求

com.alibaba.nacos.naming.remote.rpc.handler.InstanceRequestHandler#handle

发现报错了

 

 原因是我之前将这个微服务设置成了非临时实例,而现在我将它设置成了临时实例,因为临时实例才是走GRPC注册,否者是走InstancController去了,而我想看下GRPC。解决方法是停掉NACOS,将存在本地的METAdata删掉,我这是

"D:\prj_idea\Nacos\distribution\data\protocol\raft\

重启后解决。

 

IP和端口都被服务端拿到了,然后发布两个事件ClientRegisterServiceEvent  和InstanceMetadataEvent。事件发布后告知客户端注册成功,剩下的事件在服务端异步处理。

 

    @Override
    public void registerInstance(Service service, Instance instance, String clientId) throws NacosException {
//检查合法性,之前报错就从这出的
        NamingUtils.checkInstanceIsLegal(instance);
    
        Service singleton = ServiceManager.getInstance().getSingleton(service);
        if (!singleton.isEphemeral()) {
            throw new NacosRuntimeException(NacosException.INVALID_PARAM,
                    String.format("Current service %s is persistent service, can't register ephemeral instance.",
                            singleton.getGroupedServiceName()));
        }
//更改该客户端对应的client对象
        Client client = clientManager.getClient(clientId);
        if (!clientIsLegal(client, clientId)) {
            return;
        }
        InstancePublishInfo instanceInfo = getPublishInfo(instance);
        client.addServiceInstance(singleton, instanceInfo);
        client.setLastUpdatedTime();
        client.recalculateRevision();
//通知我注册完成了
        NotifyCenter.publishEvent(new ClientOperationEvent.ClientRegisterServiceEvent(singleton, clientId));
//通知我的元信息可能变了
        NotifyCenter
                .publishEvent(new MetadataEvent.InstanceMetadataEvent(singleton, instanceInfo.getMetadataId(), false));
    }

上面那两个事件在哪被处理?

第一个ClientRegisterServiceEvent

com.alibaba.nacos.naming.core.v2.index.ClientServiceIndexesManager#onEvent

 
ClientServiceIndexesManager这个类,里面两个map,估计是把注册上来的客户端的某些信息存在这,并且维护起来

存到map中后,又发了个事件ServiceChangedEvent,这个时间被NamingSubscriberServiceV2Impl监听。可以发现SmartSubscriber 、Subscriber

的实现类都是监听类

com.alibaba.nacos.naming.push.v2.NamingSubscriberServiceV2Impl#onEvent

将发生变化的客户端的最新信息推送给 订阅该客户端的其他客户端

    @Override
    public void onEvent(Event event) {
        if (event instanceof ServiceEvent.ServiceChangedEvent) {
            // If service changed, push to all subscribers.
            ServiceEvent.ServiceChangedEvent serviceChangedEvent = (ServiceEvent.ServiceChangedEvent) event;
            Service service = serviceChangedEvent.getService();
            delayTaskEngine.addTask(service, new PushDelayTask(service, PushConfig.getInstance().getPushTaskDelay()));
            MetricsMonitor.incrementServiceChangeCount(service.getNamespace(), service.getGroup(), service.getName());
        } else if (event instanceof ServiceEvent.ServiceSubscribedEvent) {
            // If service is subscribed by one client, only push this client.
            ServiceEvent.ServiceSubscribedEvent subscribedEvent = (ServiceEvent.ServiceSubscribedEvent) event;
            Service service = subscribedEvent.getService();

//将发生变化的客户端的信息推送给订阅该客户端的其他客户端
            delayTaskEngine.addTask(service, new PushDelayTask(service, PushConfig.getInstance().getPushTaskDelay(),
                    subscribedEvent.getClientId()));
        }
    }

 

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

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

相关文章

“深度学习”学习日记。与学习有关的技巧--权重的初始值

2023.1.22 在深度学习的学习中&#xff0c;权重的初始值特别重要。这关系到神经网络的学习能否成功&#xff1b; 在以前误差反向传播法和神经网络学习的算法实现这两篇文章中&#xff0c;对权重的初始值的确定是这样的&#xff1a; class TwoLayerNet:def __init__(self, inp…

在2022年的最后一天我学会了哈希表

文章目录前言STL相关容器unordered_setunordered_map哈希表哈希冲突闭散列开散列STL相关容器的模拟实现用一个哈希表改造两个容器哈希表的迭代器总结前言 首先先提前祝贺大家新年快乐&#xff01;本文是农历2022年的最后一篇博客。而今天我们介绍的也是STL里面重要的一个数据结…

2023年, 前端路上的开源总结(最新更新...)

19年至今, 笔者利用空余时间陆陆续续做了一些开源项目, 大部分开源项目都是以实际价值为开源基础, 所以我觉得有必要做一个总结和复盘,在复盘的过程中希望也能对大家有所帮助.今后笔者的开源项目都会放在这篇文章中,如果想学习的可以收藏交流.1. 基于react实现的滑动验证码组件…

[LeetCode周赛复盘] 第 96 场双周赛20230121

[LeetCode周赛复盘] 第 96 场双周赛20230121 一、本周周赛总结二、 [Easy] 6300. 最小公共值1. 题目描述2. 思路分析3. 代码实现三、[Medium] 6275. 使数组中所有元素相等的最小操作数 II1. 题目描述2. 思路分析3. 代码实现四、[Medium] 6302. 最大子序列的分数1. 题目描述2. 思…

【JavaScript】33_对象的序列化----JSON

3、对象的序列化 对象的序列化 JS中的对象使用时都是存在于计算机的内存中的 序列化指将对象转换为一个可以存储的格式 在JS中对象的序列化通常是一个对象转换为字符串&#xff08;JSON字符串&#xff09;序列化的用途&#xff08;对象转换为字符串有什么用&#xff09;&…

Linux嵌入式开发——文件系统结构

文章目录Linux嵌入式开发——文件系统结构一、根目录“/”二、Ubuntu文件系统结构三、绝对路径和相对路径Linux嵌入式开发——文件系统结构 一、根目录“/” ​ Linux下“/”就是根目录&#xff01;所有的目录都是由根目录衍生出来的。 二、Ubuntu文件系统结构 /bin 存放二进…

第十届蓝桥杯省赛 C++ A/B组 - 完全二叉树的权值

✍个人博客&#xff1a;https://blog.csdn.net/Newin2020?spm1011.2415.3001.5343 &#x1f4da;专栏地址&#xff1a;蓝桥杯题解集合 &#x1f4dd;原题地址&#xff1a;蜂巢 &#x1f4e3;专栏定位&#xff1a;为想参加蓝桥杯的小伙伴整理常考算法题解&#xff0c;祝大家都能…

Linux C编程一站式学习笔记4

Linux C编程一站式学习笔记 chap4 分支语句 文章目录Linux C编程一站式学习笔记 chap4 分支语句一.if语句语句块习题二.if/else语句引例if/else语句 语法规则if else 的配对原则习题1、写两个表达式&#xff0c;分别取整型变量x的个位和十位2、写一个函数&#xff0c;参数是整型…

常见流对象的使用

文章目录一、缓冲流字节缓冲流字符缓冲流二、转换流字符输入转换流字符输出转换流三、对象序列化对象序列化对象反序列化四、打印流PrintStreamPrintWriter一、缓冲流 缓冲流&#xff1a;也叫高效流或者高级流&#xff0c;我们之前学的字节流称为原始流&#xff0c;缓冲流自带…

【JavaSE】浅析String与StringTable

文章目录1. 前言2. String的两种创建方式2.1 通过new关键字创建一个字符串对象2.2 采用双引号的方式来创建字符串对象2.3 两种方式的区别3. StringTable的位置4. String的intern()方法5. 判断两个字符串是否相等5.1 equals5.2 1. 前言 String类是开发中经常使用的一个类。 对…

第七层:多态

文章目录前情回顾多态多态的基本概念动态多态的满足条件动态多态的使用虚函数多态的优点纯虚函数和抽象类抽象类特点虚析构和纯虚析构虚析构和纯虚析构的共性虚析构和纯虚析构的区别面向对象结束&#xff0c;接下来是什么?本章知识点&#xff08;图片形式&#xff09;&#x1…

数据结构进阶 哈希桶

作者&#xff1a;小萌新 专栏&#xff1a;数据结构进阶 作者简介&#xff1a;大二学生 希望能和大家一起进步&#xff01; 本篇博客简介&#xff1a;模拟实现高阶数据结构 哈希桶 哈希桶哈希冲突的另一种解决方法开散列 -- 链地址法举例哈希表的开散列实现 --哈希桶哈希表的结构…

自动化测试Selenium【基础篇二】

自动化测试Selenium【基础篇二】&#x1f34e;一.Selenium基础使用&#x1f352;1.1 信息打印&#x1f349; 1.1.1打印标题&#x1f349; 1.1.1打印当前网页标题&#x1f352;1.2 窗口&#x1f349;1.2.1 获取句柄&#x1f349;1.2.2 窗口切换&#x1f349;1.2.3 窗口大小设置&…

当你点击浏览器的瞬间都发生了什么----- 网络学习笔记

计算机网络前言web 浏览器协议栈创建套接字阶段。连接阶段。断开阶段。IP模块网卡网络设备 --- 集线器、交换器和路由器集线器交换器路由器路由器的附加功能一 &#xff1a;地址转换路由器的附加功能一 &#xff1a;包过滤功能互联网内部接入网光纤接入网&#xff08;FTTH&…

JDK8 前后的 Date 日期时间 API

JDK8 前后的 Date 日期时间 API 每博一文案 师父说&#xff1a;人只要活在世界上&#xff0c;就会有很多的烦恼&#xff0c;痛苦或是快乐&#xff0c;取决于逆的内心&#xff0c;只要心里拥有温暖灿烂的阳光&#xff0c; 那么悲伤又有什么好畏惧的呢&#xff1f; 人生如行路&a…

vue学习笔记(更新中)

目录 简介 使用Vue写一个"hello&#xff0c;world" 前置准备 代码书写 MVVM模型理解 插值语法和指令语法 插值语法 指令语法 指令&#xff1a;v-bind 指令&#xff1a;v-model vue中的el和data的两种写法 数据代理 方法&#xff1a;defineProperty() 说明…

新年礼物已收到!2022 Apache IoTDB Commits 数量排名 3/364!

社区喜报&#xff01;据 The Apache Software Foundation 官方 Projects Statistics&#xff08;项目信息统计网站&#xff09;的实时数据显示&#xff0c;Apache IoTDB 在过去 12 个月&#xff08;即 2022 年度&#xff09;共发表 6829 Commits&#xff0c;排名 2022 年度 Apa…

2、Three.js开发入门与调试设置

一、添加坐标轴辅助器 AxesHelper 用于简单模拟3个坐标轴的对象. 红色代表 X 轴. 绿色代表 Y 轴. 蓝色代表 Z 轴. 构造函数 AxesHelper( size : Number ) size -- (可选的) 表示代表轴的线段长度. 默认为 1. //添加坐标轴 const axesHelper new THREE.AxesHelper(5); sc…

CSS 特效之心形-彩虹-加载动画

CSS 特效之心形-彩虹-加载动画参考描述效果HTMLCSS重置元素的部分默认样式bodyli动画定义指定animationul抖动代码总汇参考 项目描述搜索引擎BingMDNMDN Web Docs 描述 项目描述Edge109.0.1518.61 (正式版本) (64 位) 效果 HTML <!DOCTYPE html> <html lang"e…

Keil C51工程转VSCode Keil Assistant开发全过程

Keil C51工程转VSCode Keil Assistant开发全过程✨这里以stc15W408AS为例。&#x1f4cc;相关篇《【开源分享】自制STC15W408AS开发板》 &#x1f4fa;编译-烧录演示&#xff1a; &#x1f4cb;转VSCODE开发环境主要原因可能代码提示以及代码跳转功能&#xff0c;或者其他。 &…