1、不同环境相同配置问题-自定义Data ID配置
在实际的开发过程中,项目所用到的配置参数有的时候并不需要根据不同的环境进行区分,生产、测试、开发环境所用到的参数值是相同的。怎么解决同一服务在多环境中,引用相同的配置的问题?Nacos Config也提供了相应的解决方案:可以通过服务名+拓展名的方式,来实现同一个微服务下不同的环境,共享的配置文件。
在Nacos配置中心添加一个Data Id为nacos-config-client.yaml的通用配置文件:
在config-3377中添加Nacos配置中心的地址:
在Controller中添加对应方法:
访问http://localhost:3377/config/configCommon,可以看到已经成功拿到对应的配置:
可以看到, nacos-config-client.yaml配置文件其实是没有带环境后缀在后面,也就是无论哪个环境都可以加载此配置文件。但是需要注意的是,也正是由于没有带环境后缀,所以这种通用的配置文件的优先级是低于指定环境的配置文件。从启动的日志中就可以看到,优先读取带环境的配置文件,再读取无环境后缀的通用配置文件:
2、不同微服务之间如何共享配置及拓展配置
当前这种配置方式是最基础的配置方式,但是在实际开发中会涉及到多个微服务之间共享配置。比如说redis地址,服务注册中心公共组件等等,那么这些组件是多个微服务共享的,所以可以使用Nacos Config提供的共享配置方式来配置共享的配置文件:
shared-configs
在Nacos配置中心添加一个Data Id为nacos-config-client.yaml的Redis配置文件:
原先配置文件的Data Id都是以服务名称来明明,但是这个配置文件是被多个服务公用的,所以取哪一个服务名称来命名都不合适,索性随便自定义一个即可。
在config-3377中添加Nacos配置中心,并指定对应的共享配置文件:
进入源码中可以看到,shared-comfigs是个列表,也就是说可以配置多个共享配置文件:
共享文件中要指定的几个参数,源码中也都写的很清楚,直接复制照搬即可:
在Controller中添加对应方法:
访问http://localhost:3377/config/configShared,可以看到已经成功拿到对应的配置:
注意:多个 Data Id 同时配置时,优先级关系是 `spring.cloud.nacos.config.extension-configs[n].data-id` 其中 n 的值越大,优先级越高。也就是说,shared-configs[1]的优先级高于shared-configs[0]的优先级。
extension-configs
其实以上的实现还可以通过extension-configs方式来完成,其实作用基本一致,只不过语义上可以更好的区分。如果需要在一个微服务上配置多个配置文件,可以使用extension-configs,如果需要多个配置文件共享,可以使用shared-configs配置方式,当然其实两种方式所实现的效果和配置方法基本一致。所以通过自定义扩展的 Data Id 配置,既可以解决多个应用间配置共享的问题,又可以支持一个应用有多个配置文件。
只需要在yml配置文件中将shared-configs改为extension-configs即可:
重启服务后,再访问http://localhost:3377/config/configShared,可以看到效果一致:
Spring Cloud Alibaba Nacos Config 目前提供了三种配置能力从 Nacos 拉取相关的配置。
- A: 通过 `spring.cloud.nacos.config.shared-configs[n].data-id` 支持多个共享 Data Id 的配置;
- B: 通过 `spring.cloud.nacos.config.extension-configs[n].data-id` 的方式支持多个扩展 Data Id 的配置;
- C: 通过内部相关规则(应用名、应用名+ Profile )自动生成相关的 Data Id 配置;
当三种方式共同使用时,它们的一个优先级关系是:A < B < C
3、Nacos动态刷新原理
什么是动态监听
所谓动态监听,简单理解就是指Nacos会自动找到那些服务已经注册,而对比来说静态监听,就是指需要有指定配置指定的服务。其实在这里就要说一下客户端和服务端的交互方式,无非就是推和拉:
- Push:表示服务端主动将数据变更信息推送给客户端
- 服务需要维持客户端的长连接,因为需要知道具体推送的客户端
- 客户端耗费内存高,因为需要保存所有客户端的连接,并且需要检测连接有效性(心跳机制)
- Pull:表示客户端主动去服务端拉取数据
- 需要定时拉取数据
- 缺点:时效性,数据实时性,无效请求
动态刷新机制
Nacos动态刷新机制,采用推和拉的优点,避免缺点。Nacos做配置中心的时候,配置数据的交互模式是有服务端push推送的,还是客户端pull拉取的?Nacos客户端发送一个请求连接到服务端,然后服务端中会有一个29.5+0.5s的一个hold期,然后服务端会将此次请求放入到allSubs队列中等待,触发服务端返回结果的情况只有两种,第一种是时间等待了29.5秒,配置未发生改变,则返回未发生改变的配置;第二种是操作Nacos Dashboard或者API对配置文件发生一次变更,此时会触发配置变更的事件,发送一条LocalDataEvent消息,此时服务端监听到消息,然后遍历allSubs队列,根据对应的groupId找到配置变更的这条ClientLongPolling任务,并且通过连接返回给客户端。
Nacos动态刷新避免了服务端对客户端进行push操作时需要保持双方的心跳连接,同样也避免了客户端对服务端进行pull操作时数据的时效性问题,不必频繁去拉去服务端的数据。通过上面原理的初步了解,显而易见,答案是:客户端主动拉取的,通长轮询的方式(Long Polling)的方式来获取配置数据。
短轮询
不管服务端的配置是否发生变化,不停发起请求去获取配置,比如支付订单场景中前端不断轮询订单支付的状态,这样的坏处显而易见,由于配置并不会频繁发生变更,如果是一直发请求,一定会对服务端造成很大的压力。还会造成数据推送的延迟,比如每10秒请求一次配置,如果在第11秒的时候配置更新,那么推送将会延迟9秒,等待下一次请求这就是短轮询,为了解决短轮询的问题,有了长轮询的方案。
长轮询
长轮询不是什么新技术,它其实就是由服务端控制响应客户端请求结果的返回时间,来减少客户端无效请求的一种优化手段。其实对于客户端来说,长轮询的使用并没有本质上的区别,客户端发起请求后,服务端不会立即返回请求结果,而是将请求hold挂起一段时间,如果此时间段内配置数据发生变更,则立即响应客户端,若一直无变更则等到指定超时时间后响应给客户端结果,客户端重新发起长链接。
4、Nacos一致性协议:Distro协议
Distro协议是Nacos社区自研的一种AP分布式协议,是面向临时实例设计的一种分布式协议,其保证在某些Nacos节点宕机后,整个临时实例处理系统依旧可以正常工作。作为一种有状态的中间件应用内嵌协议,Distro保证了各个Nacos节点对于注册请求的统一协调和储存。
Distro协议的主要设计思想
- Nacos 每个节点是平等的都可以处理写请求,同时把新数据同步到其他节点;
- 每个节点只负责部分数据,定时发送自己负责数据的校验值到其它节点来保持数据一致性;
- 每个节点独立处理读请求,及时从本地发出响应;
Nacos为什么需要一致性协议
- Nacos 在开源支持就定下了⼀个目标,尽可能的减少用户部署以及运维成本,做到用户只需要⼀个程序包,就可以快速以单机模式启动 Nacos 或者以集群模式启动 Nacos。而Nacos 是⼀个需要存储数据的⼀个组件,为了实现这个目标,就需要在 Nacos 内部实现数据存储。单机下其实问题不大,简单的内嵌关系型数据库即可;但是集群模式下,就需要考虑如何保障各个节点之间的数据⼀致性以及数据同步,而要解决这个问题,就不得不引入共识算法,通过算法来保障各个节点之间的数据的⼀致性;
- Distro 协议是阿里巴巴自研的⼀个最终⼀致性协议,而最终⼀致性协议有很多,比如Gossip(流行病协议)、Eureka 内的数据同步算法。而 Distro 算法是集 Gossip 以及 Eureka 协议的优点并加以优化而出来的。对于原生的 Gossip,由于随机选取发送消息的节点,也就不可避免的存在消息重复发送给同⼀节点的情况,增加了网络的传输的压力,也给消息节点带来额外的处理负载。而Distro算法引入了权威 Server 的概念,每个节点负责⼀部分数据以及将自己的数据同步给其它节点,有效的降低了消息冗余的问题;
Distro协议具体执行逻辑
数据初始化
新加入的 Distro 节点会进行全量数据拉取。具体操作是轮询所有的 Distro 节点,通过向其它的机器发送请求拉取全量数据。在全量拉取操作完成之后,Nacos 的每台机器上都维护了当前的所有注册上来的非持久化实例数据。
数据校验
在 Distro 集群启动之后,各台机器之间会定期的发送心跳。心跳信息主要为各个机器上的所有数据的元信息(之所以使用元信息,是因为需要保证网络中数据传输的量级维持在⼀个较低水平)。这种数据校验会以心跳的形式进行,即每台机器在固定时间间隔会向其它机器发起⼀次数据校验请求。⼀旦在数据校验过程中,某台机器发现其它机器上的数据与本地数据不⼀致,则会发起⼀次全量拉取请求,将数据补齐。
写操作
对于⼀个已经启动完成的 Distro 集群,在⼀次客户端发起写操作的流程中,当注册非持久化的实例的写请求打到某台 Nacos 服务器时,Distro 集群处理的流程包括几个部分:
1、前置的 Filter 拦截请求,并根据请求中包含的 IP 和 port 信息计算其所属的Distro 责任节点,并将该请求转发到所属的 Distro 责任节点上;
2、责任节点上的 Controller 将写请求进行解析;
3、Distro 协议定期执行 Sync 任务,将本机所负责的所有的实例信息同步到其它节点上;
读操作
由于每台机器上都存放了全量数据,因此在每⼀次读操作中,Distro 机器会直接从本地拉取数据,快速响应。 这种机制保证了 Distro 协议可以作为⼀种 AP 协议,对于读操作都进行及时的响应。在网络分区的情况下,对于所有的读操作也能够正常返回;当网络恢复时,各个 Distro 节点会把各数据分片的数据进行合并恢复。
Distro 协议是 Nacos 对于临时实例数据开发的⼀致性协议。其数据存储在缓存中,并且会在启动时进行全量数据同步,并定期进行数据校验。在 Distro 协议的设计思想下,每个 Distro 节点都可以接收到读写请求。所有的Distro协议的请求场景主要分为三种情况:
1、当该节点接收到属于该节点负责的实例的写请求时,直接写入;
2、当该节点接收到不属于该节点负责的实例的写请求时,将在集群内部路由,转发给对应的节点,从而完成读写;
3、当该节点接收到任何读请求时,都直接在本机查询并返回(因为所有实例都被同步到了每台机器上);
Distro 协议作为 Nacos的内嵌临时实例⼀致性协议,保证了在分布式环境下每个节点上面的服务信息的状态都能够及时地通知其它节点,可以维持数十万量级服务实例的存储和最终一致性。