热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

c++使用nacos_Nacos获取服务端配置和动态更新的源码分析(上)

背景前面两个springboot2.x整合nacos系列的教程里面,我们使用docker初步搭建了nacos单机版的服务端,并且介绍了我们在平时开发的过
背景

前面两个spring boot2.x整合nacos系列的教程里面,我们使用docker初步搭建了nacos单机版的服务端,并且介绍了我们在平时开发的过程中,如何正确高效的使用nacos,让nacos无限的减少我们本地的配置,本小节主要介绍我们在本地如何能够拉取到nacos服务端的配置的原理,和源码基本讲解,让大家知道其中的基本流程,并且能够掌握其中的源码技巧,希望大家在有的工作中,能够有机会去使用这些优秀源码的技巧,让我们平时的业务代码写的更加精致

tips:本小节大概阅读的时间在20分钟,分上下两小节,如果您能静下心来安心地读完,相信你会觉得受益匪浅,最后希望大家一起进步~本文为原创,如果您希望,帮忙关注一下,您的支持是我写作最大的动力~

阅读源码的入口

首先,在阅读源码之前,我们先找到阅读源码的入口,在nacos的源代码中,子项目example中有一个示例类ConfigExample,我们先运行一下,看下效果

com.alibaba.nacos.example.ConfigExample

源代码示例如下

807fec81249fc0f3f78e9f3640289855.png

最基本的main函数nacos客户端获取服务端数据方法示例

运行main函数,我们可以成功的看到控制台打印输出如下

528c5bef1ad4f279404b7fc9b0c1dac4.png

运行结果

我们粗略地看下ConfigExample的main函数实现,我们指定了nacos服务端的地址IP,dataId,groupId,namespace 然后我们就可以获取到服务端返回给我们的数据,从这一点可以明确一个远程的properties的唯一标识是如下几个因素可以确认

  1. groupId
  2. dataId
  3. namespace

我们回到刚才的源代码,我们还配置了一个listener,nacos最大的一个特性就是支持热更新,当nacos服务端的properties更新的时候,所有的客户端(具体说来,是订阅过对应dataId,groupId的客户端)都能"立即"(具体说应该是准实时)感知到服务端的数据变更,我们看下当注册一个如下的listener的时候,我们修改一下配置中心的属性,看看该listener能不能感知到变化

21439553b7545f3ff984db905dec1549.png

我们在nacos的服务端的namespace为dev,dataId为oms,groupId为product_center下修改数据,点击发布

42e29166778081cd79e7bfa0f60e0273.png

我们能发现控制台出现了我们意料之中的变化,nacos的客户端感知到了服务端的数据变化

dc99c50147e95dc7472b3bb78fe970ed.png

本章节,我们就介绍一下如下2个内容

  1. 客户端如何拉取服务端的配置信息
  2. 客户端如何感知到服务端的配置变化(重点)
阅读源码前的知识准备

1.Servlet3.0新特性,异步处理的支持

servlet3.0开始开始支持异步处理,所谓的异步处理就是,当servlet接受到请求的时候,可能需要一段耗时很长的业务处理时间,这个时候业务线程就是tomcat或者其他web容器的servlet线程,这个线程就会一直消耗在处理业务线程上来,无法得到释放,如果大量客户端请求到来的时候,web容器就没有剩余线程处理业务逻辑,这就会影响性能,所以servlet3.0开始有了一个新的特性:servlet的线程接受到请求之后,把逻辑复杂耗时时间长的操作交给异步线程,自己则回归到web容器线程池中,这样能够快速地释放servlet线程,得到资源的最大利用,异步线程处理结束之后,可以返回web服务器端的响应数据(以后有机会做一个spring boot 整个异步处理新特性的文章)

2.ScheduledExecutorService处理类的scheduledWithFixedDelay方法的含义,四个参数

  1. 第一个Runnable,表示要执行的任务函数体
  2. 第二个参数initialDelay:表示第一个任务开始执行的延迟执行时间
  3. 第三个参数delay:表示上一个任务执行结束之后,下一个任务执行的时间,这边必须要注意的是上一个任务执行完之后开始计时的延迟时间
  4. 第四个参数Timeout,时间单位

3.ScheduledExecutorService处理类的schedule方法,这个方法表示延迟执行,且只调度一次,这个方法返回一个future对象,可以通过future.get,cancel等方法进行一些操作,来达到我们要的效果

以上三个就是我们在本章需要拥有的基本知识点,在接下来的核心源码分析中,了解这些你就会有事半功倍的效果,这边先卖一个关子

阅读源码之前的思考

我个人觉得阅读源码之前,我们一定要想好为什么要阅读源码,要解决我们什么困惑,带着问题去看源码,这样我们就能在看源码的过程中,不迷失方向

1.我们之前说过nacos是典型的C/S架构,如果客户端想要获取服务端的properties配置信息,根据服务端的地址,dataId,groupId,namespace应该说发送一个http请求就可以成功获取到信息,如果这个代码给我们写是不是写一个httpClient请求就可以完成获取属性的操作了?感觉这样并不是很难

2.客户端是如何获取到nacos属性变更通知的,是服务端主动通知的,还是客户端有一个定时任务不断地去查询对应的dataId,group组下的properties是否有变更呢,其实这个问题就是我们经常说的推拉模型,按照目前所有主流框架的逻辑,应该不是推模式,因为如果使用服务端变更的时候,服务端主动通知客户端属性变更,服务端的性能会成很大的问题,首先你要保持很多的长连接,还有就是当客户端很多的时候,监听的节点数以万计的时候,服务端的性能应该就是很大的问题,所以初步定论,现代优秀的nacos框架,不应该会是推模型

至于为什么不是"推"模型,还有一个经验问题

1.zookeeper有一个watch的机制,现在zookeeper的性能一直是大家所诟病的,当zookeeper的被watch的节点很多的时候,或者watch的客户端很多的时候,因为zookeeper就是使用推模型,会导致zookeeper服务端性能急剧下降,推送的不及时

2.看过阿里巴巴另一个优秀的消息中间件RocketMQ的人应该都知道RocketMQ使用的就是拉模型,解决的就是服务端性能的问题,使用的是"长轮询"的方式,所以这边猜想,同样是阿里中间件团队,应该也会考虑到这个问题(RocketMQ的整合和源码阅读以后也会一起整理)

带着如上的2个问题就开始进行源代码的阅读,应该会很轻松

第一个问题:客户端如何获取到服务端的配置信息的?

回到我们刚才的测试代码ConfigExample的入口,可以看到源代码中使用NacosFactory的#createConfigService方法,返回了一个ConfigService

57dc55b22873d2366dcf154567aedcb9.png

ConfigService明显就是一个核心接口,方法列表如下,根据方法名,有getConfig,addListener等我们需要的一些客户端常用的方法,所以我们这边应该深入这个方法的研究

d8bb3da1c77202692f64012b45cc550d.png

ConfigService的方法列表

我们可以看到这边跟我们平时想象的不一样,一般factory的静态方法都是返回的一个单例,而这边却是使用反射创建一个继承ConfigService的实现类的NacosConfigService

a17a03ef85798364d95b146b843342ab.png

反射创建NacosConfigService

我们可以重点关注一下NacosConfigService的构造函数,源代码如下,配置好编码格式和namespace之后,我们重点看下下面源码中,红框标注的2个部分,第一个部分是用装饰者模式创建的带有统计的httpclient,名字叫做ServerHttpAgent,跟我们设想的一样,也是使用http协议与nacos服务端进行交互的,那么获取服务端的配置信息,应该并不难,然后又创建了一个ClientWorker,顾名思义,感觉这个类就是nacos客户端的总代理一样,应该所有的核心逻辑操作就在这边了,接下来我们应该会跟这个ClientWorker多次打交道

678e39a8478c4dda036662e659061392.png

回到我们刚才的main函数,获取配置属性的地方,方法名叫做getConfig的地方

10345c9ef3304d6c4e8dec81f35583b9.png

我们可以跟踪源码分析,很容易就看到具体在NacosConfigService中的实现方法getConfigInner,可以看到第一段逻辑代码就是优先获取本地的关于dataId,groupId,namespace的配置,如果能获取到则优先返回,为什么优先获取本地呢,其实nacos也是做出了性能考虑,否则每次都去查询服务器端的数据,这样会导致服务器端的压力负载过大,特别是成千上万个节点连接到服务器端的时候,所以优先读取本地配置

4c83690c47441bcab1485cdcda6d617f.png

如果本地文件不存在对应的配置,相当于本地缓存没有,这种情况一般都是第一次请求的时候,本地没有缓存,这个时候就需要请求服务端数据,接着看源码,我们可以看到接下来就是使用worker去获取服务端的数据了,也就是前面构造函数初始化好的clientworker

e2ed09661973cd3bbc723601591eacb9.png

我们感觉快接近真相大白了,我们接着看clientworker的getServerConfig方法,我们之前说的使用httpClient获取数据,这边也是这么做的,具体的参数,我在下图的源码中都一一说明标注了一下,最后请求的地址应该是http://ip:8848/v1/cs/configs的Get请求,服务端获取配置信息的源码,我们下几个小节分析,在这边我们只关注获取数据的逻辑,最后客户端成功获取到了服务器端的数据,总而言之,还是比较简单的

8f0b359ec4d090f15c8f2e88b57abfbf.png

使用httpclient去获取服务端的配置信息

接下来就有一段基本的逻辑处理,大家也不要忘记,把从服务端获取到的数据持久化到本地磁盘,这样就可以与刚才优先获取本地的配置做到相呼应了,可以看到当与服务端交互,服务端返回成功或者没找到的时候,都可以将信息持久化到本地磁盘,这样就可以很方便地优先从本地获取了

c1ee673e69fec95e2b6607bae0e94225.png

持久化到本地磁盘

到此为止,客户端获取到服务端的配置信息,主体流程已经分析结束了,接下来就要分析客户端如何感知到服务端的配置变更了

小结

本小节内容相对比较简单,也是我们阅读源码的第一篇文章,主要目的就是希望大家在阅读源码之前,有一些自己的思考,带着问题,和带着自己的想法去阅读源码,想着如果是自己应该怎么实现,这些写中间件的大牛又是怎么实现的,这样当看到别人实现的亮点的时候,以后自己就可以去模仿,去"抄写",慢慢地就会变得更加强大一点,与大牛的差距就会慢慢地缩小一些

废话不多说,还是总结一下吧,nacos客户端获取服务端的配置信息,还是比较简单的,依靠clientworker去获取,这是一个核心类,优先获取本地的配置,如果本地没有缓存,就去获取nacos服务器端的数据

下一个小节,应该就是本小节的重点内容,如何客户端如何感知到服务器端的数据变化,客户端感知到变化之后,更新本地磁盘的配置信息,这样就可以永远保持本地磁盘是最新的配置信息了~

如果您看到这里,觉得我写的不错的话,希望您点一个关注,或者你现在没有时间看的话,点一个收藏,在您闲暇的时候,翻阅看看,相信我们可以一起学习进步,如果写的有不对的地方,欢迎批评指出



推荐阅读
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • http:my.oschina.netleejun2005blog136820刚看到群里又有同学在说HTTP协议下的Get请求参数长度是有大小限制的,最大不能超过XX ... [详细]
  • 本文介绍了在Linux下安装和配置Kafka的方法,包括安装JDK、下载和解压Kafka、配置Kafka的参数,以及配置Kafka的日志目录、服务器IP和日志存放路径等。同时还提供了单机配置部署的方法和zookeeper地址和端口的配置。通过实操成功的案例,帮助读者快速完成Kafka的安装和配置。 ... [详细]
  • Servlet多用户登录时HttpSession会话信息覆盖问题的解决方案
    本文讨论了在Servlet多用户登录时可能出现的HttpSession会话信息覆盖问题,并提供了解决方案。通过分析JSESSIONID的作用机制和编码方式,我们可以得出每个HttpSession对象都是通过客户端发送的唯一JSESSIONID来识别的,因此无需担心会话信息被覆盖的问题。需要注意的是,本文讨论的是多个客户端级别上的多用户登录,而非同一个浏览器级别上的多用户登录。 ... [详细]
  • 这是原文链接:sendingformdata许多情况下,我们使用表单发送数据到服务器。服务器处理数据并返回响应给用户。这看起来很简单,但是 ... [详细]
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • 关键词:Golang, Cookie, 跟踪位置, net/http/cookiejar, package main, golang.org/x/net/publicsuffix, io/ioutil, log, net/http, net/http/cookiejar ... [详细]
  • 在重复造轮子的情况下用ProxyServlet反向代理来减少工作量
    像不少公司内部不同团队都会自己研发自己工具产品,当各个产品逐渐成熟,到达了一定的发展瓶颈,同时每个产品都有着自己的入口,用户 ... [详细]
  • 个人学习使用:谨慎参考1Client类importcom.thoughtworks.gauge.Step;importcom.thoughtworks.gauge.T ... [详细]
  • 本文介绍了将mysql从5.6.15升级到5.7.15的详细步骤,包括关闭访问、备份旧库、备份权限、配置文件备份、关闭旧数据库、安装二进制、替换配置文件以及启动新数据库等操作。 ... [详细]
  • 本文介绍了在CentOS上安装Python2.7.2的详细步骤,包括下载、解压、编译和安装等操作。同时提供了一些注意事项,以及测试安装是否成功的方法。 ... [详细]
  • 解决nginx启动报错epoll_wait() reported that client prematurely closed connection的方法
    本文介绍了解决nginx启动报错epoll_wait() reported that client prematurely closed connection的方法,包括检查location配置是否正确、pass_proxy是否需要加“/”等。同时,还介绍了修改nginx的error.log日志级别为debug,以便查看详细日志信息。 ... [详细]
  • 本文介绍了一个适用于PHP应用快速接入TRX和TRC20数字资产的开发包,该开发包支持使用自有Tron区块链节点的应用场景,也支持基于Tron官方公共API服务的轻量级部署场景。提供的功能包括生成地址、验证地址、查询余额、交易转账、查询最新区块和查询交易信息等。详细信息可参考tron-php的Github地址:https://github.com/Fenguoz/tron-php。 ... [详细]
  • Gitlab接入公司内部单点登录的安装和配置教程
    本文介绍了如何将公司内部的Gitlab系统接入单点登录服务,并提供了安装和配置的详细教程。通过使用oauth2协议,将原有的各子系统的独立登录统一迁移至单点登录。文章包括Gitlab的安装环境、版本号、编辑配置文件的步骤,并解决了在迁移过程中可能遇到的问题。 ... [详细]
  • 开发笔记:spring boot项目打成war包部署到服务器的步骤与注意事项
    本文介绍了将spring boot项目打成war包并部署到服务器的步骤与注意事项。通过本文的学习,读者可以了解到如何将spring boot项目打包成war包,并成功地部署到服务器上。 ... [详细]
author-avatar
捡耙活哟752
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有