本文转载自微信公众号《程序员的内幕》,作者为程序员的内幕。转载本文请联系程序员内电史公众号。大家好,我是小付~Nacos大家应该不陌生。我出生在阿里,名声很好。它可以进行动态服务发现和配置管理。这是一个非常有用的工具。但是,使用这项技术的人越多,在面试中被问到的概率就越大。如果仅仅停留在使用层面,那么面试可能会吃大亏。比如我们今天要讨论的话题,Nacos作为配置中心时,配置数据的交互方式是服务端推送还是客户端主动拉取?这里我先给出答案:客户端主动拉!接下来,我们扒一扒Nacos的源码,看看它是如何实现的?在说配置中心的Nacos之前,先简单回顾一下配置中心的由来。配置中心的作用简单理解就是统一管理配置。配置修改后,应用无需重启即可动态感知。因为在传统项目中,大多采用静态配置,即配置信息写在应用程序中的yml或properties等文件中。如果要修改某个配置,通常需要重启应用才能生效。但是在某些场景下,比如我们希望在应用运行时通过修改某个配置项来实时控制某个功能的开启和关闭。频繁重启应用程序肯定是不能接受的。特别是在微服务架构下,我们应用服务拆分的粒度非常细,小到几十个,大到上百个服务,每个服务都会有自己独特或通用的配置。如果这时候我要改通用配置,是不是需要把上百个服务配置一个一个改?显然这是不可能的。因此,为了解决此类问题,配置中心应运而生。配置中心推拉模型客户端和配置中心之间的数据交互其实有两种方式,要么推,要么拉。推送模式客户端与服务器建立TCP长连接,当服务器的配置数据发生变化时,立即通过建立的长连接将数据推送给客户端。优点:长链接的优点是实时性。一旦数据发生变化,变化的数据会立即推送到客户端。对于客户端来说,这个方法比较简单。只需要建立连接就可以接收数据,不需要关心数据是否有变化。处理这种类型的逻辑。缺点:长连接可能因为网络问题不可用,俗称假死。连接状态正常,但实际上已经无法通信了。因此需要一个心跳机制KeepAlive来保证连接的可用性,从而保证配置数据的成功推送。拉取模型客户端主动向服务端发送请求拉取配置数据。常用的方法是轮询,比如每3秒向服务器请求一次配置数据。轮询的优点是实现起来比较简单。但缺点也很明显。轮询不能保证数据的实时性。什么时候申请?我们在长轮询开始时给出了答案。nacos采用客户端主动拉取的拉取模式,采用长轮询(LongPolling)获取配置数据。嗯?我以前只听说过投票。什么是长轮询?它与传统意义上的轮询有何不同(为了便于比较,我们称之为短轮询)?短轮询不管服务器配置数据是否有变化,不断发起请求获取配置。比如支付场景,前面的JS轮询订单支付状态。这样做的缺点很明显,因为配置数据不经常变化,如果一直发送请求,势必会给服务器带来很大的压力。也会造成推送数据的延迟。例如,每10s请求一次配置。如果配置在11s更新,推送会延迟9s等待下一次请求。为了解决短轮询的问题,出现了长轮询方案。长轮询长轮询并不是一项新技术。只是服务端控制响应客户端请求的返回时间,减少客户端无效请求的一种优化方法。其实对于client来说,和shortpolling是不一样的。使用上没有本质区别。客户端发起请求后,服务端不会立即返回请求结果,而是会暂停一段时间请求。如果服务器数据在此期间发生变化,它会立即响应客户端请求。如果没有变化,会等到指定的超时一定时间后才响应请求,客户端重新发起长连接。第一次接触Nacos,为了方便后续的演示操作,在本地搭建了一个Nacos。注意:运行时有一个小坑。由于Nacos默认是集群启动的,而本地构建通常是单机模式,这里需要在启动脚本startup.X中手动更改启动模式。直接执行/bin/startup.X即可,默认用户密码为nacos。几个概念Nacos配置中心的几个核心概念:dataId、group、namespace,它们的层级关系如下:dataId:是配置中心最基本的单元,是一个key-value结构,key通常是我们的配置文件name,如:application.yml,mybatis.xml,value是整个文件的内容。目前支持JSON、XML、YAML等配置格式。group:dataId配置的分组管理。比如在同一个dev环境下开发,但是同一个环境的不同分支需要不同的配置数据。这种情况下可以使用组隔离,默认组为DEFAULT_GROUP。namespace:在项目开发过程中,肯定会有dev、test、pro等多个不同的环境。命名空间是为了隔离不同的环境。默认情况下,所有配置都是公开的。架构设计下图简单描述了nacos配置中心的架构流程。客户端和控制台通过发送Http请求将配置数据注册到服务器,服务器将数据持久化到Mysql。客户端拉取配置数据,批量设置dataId监听,发起长轮询请求。如果服务器配置项发生变化,它会立即响应请求。如果没有数据变化,请求会被暂停一段时间,直到达到超时时间。为了减轻服务器的压力,保证配置中心的可用性,拉取配置数据的客户端会在本地文件中保存一个快照,并先读取。这里我省略了更多的细节,比如认证、负载均衡、高可用设计(其实这部分真的很值得学习,后面会在另一篇文章中讲到),主要是为了理清客户端之间的数据交互和服务器模型。接下来我们分析Nacos2.0.1版本的源码。2.0之后的版本变化较多,与网上很多资料略有不同。地址:https://github.com/alibaba/nacos/releases/tag/2.0.1客户端源码分析Nacos配置中心的客户端源码在nacos-client项目中,其中NacosConfigService实现类是核心所有操作的入口点。说之前,先了解一下客户端的cacheMap数据结构。说到这里,大家应该记住了,因为它几乎贯穿了Nacos客户端的所有操作。由于多线程场景的存在,为保证数据一致性,cacheMap使用AtomicReference原子变量实现。/***groupKey->cacheData.*/privatefinalAtomicReference