Nacos配置中心数据同步原理
一、Nacos概览
Nacos 能帮助我们发现、配置和管理微服务,提供了一组简单易用的特性集,帮助我们快速实现动态服务发现、服务配置、服务元数据及流量管理。
关键特性
- 服务发现和服务健康监测
- 动态配置服务
- 动态 DNS 服务
- 服务及其元数据管理
持久化
默认情况下,Nacos使用嵌入式数据库实现数据的存储。所以,如果启动多个默认配置下的Nacos节点,数据存储是存在一致性问题的。为了解决这个问题,Nacos采用了集中式存储的方式来支持集群化部署,目前只支持MySQL的存储。
同步原理
大概的原理就是Nacos 服务端保存了配置信息,客户端连接到服务端之后,根据 dataID,group可以获取到具体的配置信息,当服务端的配置发生变更时,客户端会收到通知,拿到变更后的最新配置信息。
那么问题来了,这里是服务端推给客户端,还是客户端主动去服务端拉?
二、源码解析
ConfigService
既然是配置中心,当然是要从配置中心最重要的一个接口ConfigService
入手,下面看一下它有哪些方法。
public interface ConfigService {
//拉取配置信息
String getConfig(String dataId, String group, long timeoutMs) throws NacosException;
//获取配置信息并注册监听器
String getConfigAndSignListener(String dataId, String group, long timeoutMs, Listener listener) throws NacosException;
//新增一个监听器,当服务端发生数据修改时,客户端会触发监听器回调,推荐使用异步线程
void addListener(String dataId, String group, Listener listener) throws NacosException;
//推送配置信息
boolean publishConfig(String dataId, String group, String content) throws NacosException;
//移除配置信息
boolean removeConfig(String dataId, String group) throws NacosException;
//移除监听器
void removeListener(String dataId, String group, Listener listener);
//获取服务端状态
String getServerStatus();
ConfigFactory
接着看一下是如何创建这个ConfigService
实例的。找到ConfigFactory
工厂类,可以看出这里是通过反射调用了实现类NacosConfigService
的构造方法来创建ConfigService
实例。
public class ConfigFactory {
public static ConfigService createConfigService(Properties properties) throws NacosException {
try {
Class<?> driverImplClass = Class.forName("com.alibaba.nacos.client.config.NacosConfigService");
Constructor constructor = driverImplClass.getConstructor(Properties.class);
ConfigService vendorImpl = (ConfigService) constructor.newInstance(properties);
return vendorImpl;
} catch (Throwable e) {
throw new NacosException(NacosException.CLIENT_INVALID_PARAM, e);
}
}
//...
}
NacosConfigService
接着看一下这个实现类NacosConfigService
,这里我们只关注与数据同步有关的部分,先看构造方法,具体实现方法等实例化完再看。
public class NacosConfigService implements ConfigService {
//超时时间,看下面的单位应该是30秒
private static final long POST_TIMEOUT = 3000L;
/**
* http agent http代理
*/
private HttpAgent agent;
/**
* longpolling 长轮询
*/
private ClientWorker worker;
//过滤器调用链
private ConfigFilterChainManager configFilterChainManager = new ConfigFilterChainManager();
//构造函数,入参是配置信息
public NacosConfigService(Properties properties) throws NacosException {
String encodeTmp = properties.getProperty(PropertyKeyConst.ENCODE);
if (StringUtils.isBlank(encodeTmp)) {
encode = Constants.ENCODE;
} else {
encode = encodeTmp.trim();
}
initNamespace(properties);
//实例化MetricsHttpAgent对象
//装饰者模式,两个类都实现了HttpAgent接口,MetricsHttpAgent实际也是调用了ServerHttpAgent内部的方法
agent = new MetricsHttpAgent(new ServerHttpAgent(properties));
//这个start方法等会再看
agent.start();
//实例化ClientWorker对象
worker = new ClientWorker(agent, configFilterChainManager, properties);
}
下面重点看一下MetricsHttpAgent
、ServerHttpAgent
、ClientWorker
这几个类的实现。
MetricsHttpAgent
这个类其实只是在ServerHttpAgent
的基础上做了一层修饰,看起来应该是加了监控之类的东西,重点的实现还是在ServerHttpAgent
,所以这个类不多讲,直接看ServerHttpAgent
。
agent = new MetricsHttpAgent(new ServerHttpAgent(properties));
ServerHttpAgent
这个类重点看start
、httpGet
、httpPost
、httpDelete
这几个方法。
start
调用ServerListManager
里的start
方法
public synchronized void start() throws NacosException {
serverListMgr.start();
}
启动了一个间隔30秒的定时任务,接着看一下线程池里面做了什么。
public synchronized void start() throws NacosException {
if (isStarted || isFixed) {
return;
}
GetServerListTask getServersTask = new GetServerListTask(addressServerUrl);
//省略
TimerService