1 原理思想
Nacos 采用的是 Pull 模式,但并不是简单的 Pull,而是一种长轮训机制,它结合 Push 和 Pull 两者的优势(nacos并没有push)。客户端采用长轮训的方式定时发起 Pull 请求,去检查服务端配置信息是否发生了变更,如果发生了变更,则客户端会根据变更的数据获得最新的配置。所谓的长轮训,是客户端发起轮训请求之后,服务端如果有配置发生变更,就直接返回。
如果客户端发起 Pull 请求后,发现服务端的配置和客户端的配置是保持一致的,那么服务端会先 “Hold” 住这个请求,也就是服务端拿到这个连接之后在指定的时间段内一直不返回结果,直到这段时间内配置发生变化,服务端会把原来 “Hold” 住的请求进行返回。Nacos 服务端收到请求之后,先检查配置是否发生了变更,如果没有,则设置一个定时任务,延期 29.5s 执行,并且把当前的客户端长轮训加入 allSubs 队列。这个时候有两种方式触发该链接结果的返回:// 后面单独看config server的源码解析
第一种是在等待 29.5s 后触发自动检查机制,这个时候不管配置有没有发生变化,都会把结果返回客户端。而 29.5s 就是这个长连接保持的时间。
第二种是在 29.5s 内任意一个时刻,通过 Nacos Dashboard 或者 API 的方式对配置进行了修改,这会触发一个事件机制,监听该事件的任务会遍历 allSubs 队列,找到发生变更的配置项对应的 ClientLongPolling 任务,将变更的数据通过该任务的连接进行返回,就完成一次 “推送” 操作。
这样既能够保证客户端实时感知配置的变化,也降低了服务端的压力。其中,这个长连接的回话超时时间默认是 30s。
2 实现:当远程文件发生变更后【 长轮询】
--->com.alibaba.nacos.client.config.impl.ClientWorker.LongPollingRunnable 实现
3 源码分析
3.1系统启动 注册listener
通过:初始化---->使用默认的publisher;
springboot 是配置的天堂所以一定有个ConfigAutoConfiguration+ConfigBeanDefinitionRegistrar的组合
--->org.springframework.context.support.AbstractApplicationContext#invokeBeanFactoryPostProcessors // bean定义
--->org.springframework.context.support.PostProcessorRegistrationDelegate#invokeBeanDefinitionRegistryPostProcessors // bean定义
--->org.springframework.context.annotation.ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry //bean定义
--->org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsFromRegistrars // 启动类注解入口
--->com.alibaba.nacos.spring.context.annotation.config.NacosConfigBeanDefinitionRegistrar#registerBeanDefinitions
----// 配置 来源com/alibaba/boot/nacos-config-spring-boot-autoconfigure/0.2.7/nacos-config-spring-boot-autoconfigure-0.2.7.jar!/META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.alibaba.boot.nacos.config.autoconfigure.NacosConfigAutoConfiguration
[com.alibaba.boot.nacos.config.autoconfigure.NacosConfigAutoConfiguration]
--->import
[com.alibaba.nacos.spring.context.annotation.config.NacosConfigBeanDefinitionRegistrar]
--->com.alibaba.nacos.spring.util.NacosBeanUtils#invokeNacosPropertySourcePostProcessor
--->com.alibaba.nacos.spring.core.env.NacosPropertySourcePostProcessor#postProcessBeanFactory
--->com.alibaba.nacos.spring.core.env.NacosPropertySourcePostProcessor#processPropertySource
--->com.alibaba.nacos.spring.core.env.NacosPropertySourcePostProcessor#addListenerIfAutoRefreshed
--->com.alibaba.nacos.spring.context.event.config.EventPublishingConfigService#addListener // [DelegatingEventPublishingListener] 做监听者
--->com.alibaba.nacos.spring.factory.CacheableEventPublishingNacosServiceFactory // 事件发布工厂
--->com.alibaba.nacos.spring.context.event.config.EventPublishingConfigService // 完成监听实例化
3.2 Bean 配置属性注册到Map>
-->org.springframework.context.support.AbstractApplicationContext#registerBeanPostProcessors
-->org.springframework.context.support.PostProcessorRegistrationDelegate#registerBeanPostProcessors
-->org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean
-->org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization
-->org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization
-->org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization
-->com.alibaba.nacos.spring.context.annotation.config.NacosValueAnnotationBeanPostProcessor#postProcessBeforeInitialization
-->com.alibaba.nacos.spring.context.annotation.config.NacosValueAnnotationBeanPostProcessor#doWithFields
-->com.alibaba.nacos.spring.context.annotation.config.NacosValueAnnotationBeanPostProcessor#doWithAnnotation
[com.alibaba.nacos.spring.context.annotation.config.NacosValueAnnotationBeanPostProcessor.NacosValueTarget]
// 完成-->com.alibaba.nacos.spring.context.annotation.config.NacosValueAnnotationBeanPostProcessor#doWithFields 初始化到改MAP中
3.3 长轮询监听到变更
立即刷新CacheData,调用listener [DelegatingEventPublishingListener]监听者发送context事件
// 当收到推送事件之后---->包装容器上下文事件----->主动更新MAP对应的bean实例属性
--->com.alibaba.nacos.spring.context.event.config.DelegatingEventPublishingListener#receiveConfigInfo
--->com.alibaba.nacos.spring.context.event.config.DelegatingEventPublishingListener#publishEvent
--->com.alibaba.nacos.spring.context.event.DeferredApplicationEventPublisher // 使用默认的publisher
--->com.alibaba.nacos.spring.context.event.DeferredApplicationEventPublisher#publishEvent // 触发容器事件
--->com.alibaba.nacos.spring.context.annotation.config.NacosValueAnnotationBeanPostProcessor#onApplicationEvent
因为有着Map记录了,占位符和所属实例的关系,所以依靠反射轻松改变了bean的配置值
--->com.alibaba.nacos.spring.context.annotation.config.NacosValueAnnotationBeanPostProcessor#setField
--->ReflectionUtils.makeAccessible(field);
field.set(bean, convertIfNecessary(field, propertyValue));
4 完成配置动态刷新
依靠反射轻松改变了bean的配置值
5 小结:
注意LOGGER 依赖关系,nacos输出日志
springboot 引入starter常用套路