由于微服务引入至今,相信大部分公司的小伙伴都已经对微服务的使用有了一定的熟练度了,那么每个项目中肯定都有属于自己的配置文件,小企业呢对于配置的规范肯定不是那么严谨,一味的在一个或者多个配置文件中添加,那么多个微服务呢?有需要重复造轮子了,这是其一,其二呢,配置文件经常需要修改,比如什么阿里云、redis、kafka等一系列的中间件会随着不通环境(测试环境、开发环境、生产环境)的不同而改变,这种情况下,总不能把每个微服务模块逐一去启动吧,先不说启动的过程中会造成服务的卡顿而使得用户体验不佳,这么多服务启动也是耗时耗力的
好了,这就是背景接下来引入主题:
在我们的微服务的运行环境中,每一个微服务往往不会只有一套环境,在企业中往往存在至少三套运行环境:开发、测试、生产
在这套微服务系统中,会存在三个问题:
业界常见的配置中心:
表示服务端主动将数据变更推送更新到客户端
如下图所示,如果有多个客户端时,服务端和客户端需要保持长链接,并且需要存放多个客户端的连接映射地址,还需要保持客户端与服务端的心跳机制
客户端主动去服务端拉取数据
由于为了保证数据的时效性,客户端需要频繁去拉去服务端的数据,而在服务端长时间未更新的情况下,存在许多无效的Pull
抛出一个问题,Nacos做配置中心的时候,配置数据的交互模式是有服务端push推送的,还是客户端pull拉取的?
Nacos客户端发送一个请求连接到服务端,然后服务端中会有一个29.5+0.5s的一个hold期,然后服务端会将此次请求放入到allSubs队列中等待,触发服务端返回结果的情况只有两种,第一种是时间等待了29.5秒,配置未发生改变,则返回未发生改变的配置;第二种是操作Nacos Dashboard或者API对配置文件发生一次变更,此时会触发配置变更的事件,发送一条LocalDataEvent消息,此时服务端监听到消息,然后遍历allSubs队列,根据对应的groupId找到配置变更的这条ClientLongPolling任务,并且通过连接返回给客户端
Nacos动态刷新避免了服务端对客户端进行push操作时需要保持双方的心跳连接,同样也避免了客户端对服务端进行pull操作时数据的时效性问题,不必频繁去拉去服务端的数据
通过上面原理的初步了解,显而易见,答案是:客户端主动拉取的,通长轮询的方式(Long Polling)的方式来获取配置数据
不管服务端的配置是否发生变化,不停发起请求去获取配置,比如支付订单场景中前端JS不断轮询订单支付的状态
这样的坏处显而易见,由于配置并不会频繁发生变更,如果是一直发请求,一定会对服务端造成很大的压力。还会造成数据推送的延迟,比如每10秒请求一次配置,如果在第11秒的时候配置更新,那么推送将会延迟9秒,等待下一次请求
这就是短轮询,为了解决短轮询的问题,有了长轮询的方案
长轮询不是什么新技术,它其实就是由服务端控制响应客户端请求结果的返回时间,来减少客户端无效请求的一种优化手段,其实对于客户端来说,短轮询的使用并没有本质上的区别
客户端发起请求后,服务端不会立即返回请求结果,而是将请求hold挂起一段时间,如果此时间段内配置数据发生变更,则立即响应客户端,若一直无变更则等到指定超时时间后响应给客户端结果,客户端重新发起长链接
首先这里用springcloud nacos cofig为例
注意点:需要用bootstrap.yml/properties,因为优先级最高,建议不要使用application等配置文件形式创建
server:
port: 7104
spring:
main:
allow-bean-definition-overriding: true
application:
name: iov-base
cloud:
nacos:
#配置管理
config:
enabled: true #是否使用 nacos 缺省值true
file-extension: yml #data id后缀
group: iov #组
#扩展 配置文件
#security数据库
extension-configs:
- data-id: base.yml
group: root
refresh: true #动态刷新
- data-id: vota-common.yml
group: public-config
refresh: true #动态刷新
- data-id: data-source-iov-base.yml
group: public-config
refresh: true #动态刷新
#缓存
- dataId: cachesync.yml
group: public-config
refresh: true
#消息中间件
- dataId: MOM.yml
group: public-config
refresh: true
#服务发现
- dataId: server-discovery.yml
group: public-config
refresh: true
#对象存储
- dataId: storage.yml
group: public-config
refresh: true
以为这样就完事了,这些都是springcloud config理论上的东西,我们的需求是通过改变nacos上的value值在不重启服务的情况达到自动刷新读取到最新的配置,而上面那样的配置只能说可以支持自动刷新配置读取到了容器中了,但是不会改变正在运行的服务
其实要知道自动刷新配置的原理,首先我们是通过长轮询+主动去Pull拉模式方式从nacos服务端获取配置的
我们在首次启动服务的时候,其实spring已经加载了nacos的jar,此时spring是有监听器的,通过定时去获取nacos的配置是否发生了变化,因为上面说了我们是通过长轮询+主动去Pull拉模式方式从nacos服务端获取配置的,那么肯定是缓存在本地的,如果spring启动一个定时任务专门在本地进行比较就会发现value值是改变的,然后监听到了这个事件就会通过发布事件去处理实现自动刷新配置业务,只会更新@RefreshScope和@Component + @ConfigurationProperties注解下的类的,其他的不会更新的
使用@Value时,所在的类,需要加上@RefreshScope;不然配置不会自动刷新。
示例:
如果使用实体接收nacos配置,不添加@RefreshScope,也会自动刷新。
示例: