Nacos做配置中心的时候,配置数据的交互模式是有服务端push推送的,还是客户端pull拉取的?
短轮询
不管服务端的配置是否发生变化,不停发起请求去获取配置,比如支付订单场景中前端JS不断轮询订单支付的状态
这样的坏处显而易见,由于配置并不会频繁发生变更,如果是一直发请求,一定会对服务端造成很大的压力。还会造成数据推送的延迟,比如每10秒请求一次配置,如果在第11秒的时候配置更新,那么推送将会延迟9秒,等待下一次请求
这就是短轮询,为了解决短轮询的问题,有了长轮询的方案
长轮询
长轮询不是什么新技术,它其实就是由服务端控制响应客户端请求结果的返回时间,来减少客户端无效请求的一种优化手段,其实对于客户端来说,短轮询的使用并没有本质上的区别
客户端发起请求后,服务端不会立即返回请求结果,而是将请求hold挂起一段时间,如果此时间段内配置数据发生变更,则立即响应客户端,若一直无变更则等到指定超时时间后响应给客户端结果,客户端重新发起长链接
Nacos实现
Nacos客户端发送一个请求连接到服务端,然后服务端中会有一个29.5+0.5s的一个hold期,然后服务端会将此次请求放入到allSubs队列中等待,触发服务端返回结果的情况只有两种,第一种是时间等待了29.5秒,配置未发生改变,则返回未发生改变的配置;第二种是操作Nacos Dashboard或者API对配置文件发生一次变更,此时会触发配置变更的事件,发送一条LocalDataEvent消息,此时服务端监听到消息,然后遍历allSubs队列,根据对应的groupId找到配置变更的这条ClientLongPolling任务,并且通过连接返回给客户端
Nacos动态刷新避免了服务端对客户端进行push操作时需要保持双方的心跳连接,同样也避免了客户端对服务端进行pull操作时数据的时效性问题,不必频繁去拉去服务端的数据
通过上面原理的初步了解,显而易见,答案是:客户端主动拉取的,通长轮询的方式(Long Polling)的方式来获取配置数据
坑点
我们的需求是通过改变nacos上的value值在不重启服务的情况达到自动刷新读取到最新的配置,而正常配置只能说可以支持自动刷新配置读取到了容器中了,但是不会改变正在运行的服务
其实要知道自动刷新配置的原理,首先我们是通过长轮询+主动去Pull拉模式方式从nacos服务端获取配置的
我们在首次启动服务的时候,其实spring已经加载了nacos的jar,此时spring是有监听器的,通过定时去获取nacos的配置是否发生了变化,因为上面说了我们是通过长轮询+主动去Pull拉模式方式从nacos服务端获取配置的,那么肯定是缓存在本地的,如果spring启动一个定时任务专门在本地进行比较就会发现value值是改变的,然后监听到了这个事件就会通过发布事件去处理实现自动刷新配置业务,只会更新和@注解下的类的,其他的不会更新的
- 上面是RefreshScope的源码,该注解被@Scope注解使用,@Scope用来比较Spring Bean的作用域。
- 注解的属性proxyMode默认使用TARGET_CLASS作为代理。
原理解析
为了实现动态刷新配置,主要就是想办法达成以下两个核心目标:
- 让Spring容器重新加载Environment环境配置变量
- Spring Bean重新创建生成
@RefreshScope主要就是基于@Scope注解的作用域代理的基础上进行扩展实现的,加了@RefreshScope注解的类,在被Bean工厂创建后会加入自己的refresh scope 这个Bean缓存中,后续会优先从Bean缓存中获取,当配置中心发生了变更,会把变更的配置更新到spring容器的Environment中,并且同事bean缓存就会被清空,从而就会从bean工厂中创建bean实例了,而这次创建bean实例的时候就会继续经历这个bean的生命周期,使得@Value属性值能够从Environment中获取到最新的属性值,这样整个过程就达到了动态刷新配置的效果。
@Scope
我们平常所使用的都为或是,这两个是硬编码的
@scope为refresh的时候,spring是如何创建bean的
RefreshScope继承成了GenericScope类,最终调用的的是GenericScope的get方法
GenericScope 里面 包装了一个内部类 BeanLifecycleWrapperCache 来对加了 @RefreshScope 从而创建的对象进行缓存,使其在不刷新时获取的都是同一个对象。(这里你可以把BeanLifecycleWrapperCache 想象成为一个大Map 缓存了所有@RefreshScope 标注的对象)
事件监听发布
Nacos在启动的时候会为每个配置文件注册监听事件
找下receiveConfigInfo方法什么时候触发的
继续往上找
RefreshEvent事件发布后,肯定有监听的方法
调用handel方法
可以看到最终又回到GenericScope这里来,refresh方法最终调用destroy方法,清空之前缓存的bean
总结:ContextRefresher 就是外层调用方法用的,GenericScope 里面的 get 方法负责对象的创建和缓存,destroy 方法负责再刷新时缓存的清理工作
综上所述,来总结下@RefreshScope 实现流程
1.需要动态刷新的类标注@RefreshScope 注解
2.@RefreshScope 注解标注了@Scope 注解,并默认了ScopedProxyMode.TARGET_CLASS; 属性,此属性的功能就是在创建一个代理,在每次调用的时候都用它来调用GenericScope get 方法来获取对象
3.如属性发生变更则会发布RefreshEvent刷新事件,监听者会调用 ContextRefresher refresh() ->RefreshScope refreshAll() 进行缓存清理方法调用,并发送刷新事件通知 -> GenericScope 真正的 清理方法destroy() 实现清理缓存
4.在下一次使用对象的时候,会调用GenericScope get(String name, ObjectFactory<?> objectFactory) 方法创建一个新的对象,并存入缓存中,此时新对象因为Spring 的装配机制就是新的属性了。
到此这篇nacos配置中心动态刷新(nacos配置中心动态刷新不生效)的文章就介绍到这了,更多相关内容请继续浏览下面的相关推荐文章,希望大家都能在编程的领域有一番成就!版权声明:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若内容造成侵权、违法违规、事实不符,请将相关资料发送至xkadmin@xkablog.com进行投诉反馈,一经查实,立即处理!
转载请注明出处,原文链接:https://www.xkablog.com/qkl-jr/11680.html