动态配置管理是 Nacos 的三大功能之一,通过动态配置服务,我们可以在所有环境中以集中和动态的方式管理所有应用程序或服务的配置信息。
动态配置中心可以实现配置更新时无需重新部署应用程序和服务即可使相应的配置信息生效,这极大了增加了系统的运维能力。
动态配置
下面我将来和大家一起来了解下 Nacos 的动态配置的能力,看看 Nacos 是如何以简单、优雅、高效的方式管理配置,实现配置的动态变更的。
环境准备
首先我们要准备一个 Nacos 的服务端,现在有两种方式获取 Nacos 的服务端:- 通过源码编译
- 下载 Release 包
mvn -Prelease-nacos clean install -U执行成功之后你将会看到如下图所示的结果:
然后在项目的 distribution 目录下我们就可以找到可执行程序了,包括两个压缩包,这两个压缩包就是nacos 的 github 官网上发布的 Release 包。
接下来我们把编译好的两个压缩包拷贝出来,然后解压出来直接使用,这样就相当于我们下载了 Release 包了。解压后文件结构和 nacos-server-0.8.0 一样,我们直接执行 startup.sh 即可启动一个单机的 Nacos 服务端了。启动服务端
执行下列命令来启动一个 Nacos 服务端:sh startup.sh -m standalone启动完你将会看到如下图所示的结果:启动成功后,我们就可以访问 Nacos 的控制台了,如下图所示:控制台做了简单的权限控制,默认的账号和密码都是 nacos。登录进去之后,是这样的:
新建配置
接下来我们在控制台上创建一个简单的配置项,如下图所示:启动客户端
当服务端以及配置项都准备好之后,就可以创建客户端了,如下图所示新建一个 Nacos 的 ConfigService 来接收数据:执行后将打印如下信息:这里我用了一个 System.in.read() 方法来监听输入的信息,主要是为了防止主线程退出,看不到后续的结果。修改配置信息
接下来我们在 Nacos 的控制台上将我们的配置信息改为如下图所示:修改完配置,点击 “发布” 按钮后,客户端将会收到最新的数据,如下图所示:至此一个简单的动态配置管理功能已经讲完了,删除配置和更新配置操作类似,这里不再赘述。适用场景
了解了动态配置管理的效果之后,我们知道了大概的原理了,Nacos 服务端保存了配置信息,客户端连接到服务端之后,根据 dataID,group可以获取到具体的配置信息,当服务端的配置发生变更时,客户端会收到通知。当客户端拿到变更后的最新配置信息后,就可以做自己的处理了,这非常有用,所有需要使用配置的场景都可以通过 Nacos 来进行管理。可以说 Nacos 有很多的适用场景,包括但不限于以下这些情况:
数据库连接信息
- 限流规则和降级开关
- 流量的动态调度
推还是拉
现在我们了解了 Nacos 的配置管理的功能了,但是有一个问题我们需要弄明白,那就是 Nacos 客户端是怎么实时获取到 Nacos 服务端的最新数据的。其实客户端和服务端之间的数据交互,无外乎两种情况:
- 服务端推数据给客户端
- 客户端从服务端拉数据
创建 ConfigService
从我们的 demo 中可以知道,首先是创建了一个 ConfigService。而 ConfigService 是通过 ConfigFactory 类创建的,如下图所示:
可以看到实际是通过反射调用了 NacosConfigService 的构造方法来创建 ConfigService 的,而且是有一个 Properties 参数的构造方法。需要注意的是,这里并没有通过单例或者缓存技术,也就是说每次调用都会重新创建一个 ConfigService的实例。实例化 ConfigService
现在我们来看下 NacosConfigService 的构造方法,看看 ConfigService 是怎么实例化的,如下图所示:实例化时主要是初始化了两个对象,他们分别是:- HttpAgent
- ClientWorker
HttpAgent
其中 agent 是通过装饰着模式实现的,ServerHttpAgent 是实际工作的类,MetricsHttpAgent 在内部也是调用了 ServerHttpAgent 的方法,另外加上了一些统计操作,所以我们只需要关心 ServerHttpAgent 的功能就可以了。agent 实际是在 ClientWorker 中发挥能力的,下面我们来看下 ClientWorker 类。ClientWorker
以下是 ClientWorker 的构造方法,如下图所示:可以看到 ClientWorker 除了将 HttpAgent 维持在自己内部,还创建了两个线程池:第一个线程池是只拥有一个线程用来执行定时任务的 executor,executor 每隔 10ms 就会执行一次 checkConfigInfo() 方法,从方法名上可以知道是每 10 ms 检查一次配置信息。第二个线程池是一个普通的线程池,从 ThreadFactory 的名称可以看到这个线程池是做长轮询的。现在让我们来看下 executor 每 10ms 执行的方法到底是干什么的,如下图所示:可以看到,checkConfigInfo 方法是取出了一批任务,然后提交给 executorService 线程池去执行,执行的任务就是 LongPollingRunnable,每个任务都有一个 taskId。现在我们来看看 LongPollingRunnable 做了什么,主要分为两部分,第一部分是检查本地的配置信息,第二部分是获取服务端的配置信息然后更新到本地。1、本地检查首先取出与该 taskId 相关的 CacheData,然后对 CacheData 进行检查,包括本地配置检查和监听器的 md5 检查,本地检查主要是做一个故障容错,当服务端挂掉后,Nacos 客户端可以从本地的文件系统中获取相关的配置信息,如下图所示:通过跟踪 checkLocalConfig 方法,可以看到 Nacos 将配置信息保存在了~/nacos/config/fixed-{address}8848nacos/snapshot/DEFAULT_GROUP/{dataId}这个文件中,我们看下这个文件中保存的内容,如下图所示:2、服务端检查然后通过 checkUpdateDataIds() 方法从服务端获取那些值发生了变化的 dataId 列表,通过 getServerConfig 方法,根据 dataId 到服务端获取最新的配置信息,接着将最新的配置信息保存到 CacheData 中。最后调用 CacheData 的 checkListenerMd5 方法,可以看到该方法在第一部分也被调用过,我们需要重点关注一下。可以看到,在该任务的最后,也就是在 finally 中又重新通过 executorService 提交了本任务。