本文收录于专栏 Nacos
推荐阅读:Nacos 架构 & 原理
⚠️:使用的Nacos版本为2.3.X
前言
上篇文章我们简单看了看Nacos客户端在启动的时候,初始化本地配置的大致流程。
本篇我们开始逐渐深入细节,先从客户端和服务端交互的相关代码入手。
一、NacosConfigLoader
上篇中我们也提到了客户端从服务端获取配置的代码:
这个nacosConfig
就是从服务端获取到的,我们看下这个load()
做了什么。
public String load(String dataId, String groupId, Properties nacosProperties) throws RuntimeException {
try {
//获取ConfigService
configService = nacosServiceFactory != null
? nacosServiceFactory.createConfigService(nacosProperties)
: NacosFactory.createConfigService(nacosProperties);
}
catch (NacosException e) {
throw new RuntimeException("ConfigService can't be created with dataId :"
+ dataId + " , groupId : " + groupId + " , properties : "
+ nacosProperties, e);
}
//拿到服务端的配置内容
return NacosUtils.getContent(configService, dataId, groupId);
}
NacosUtils.getContent
的实现逻辑就是从configService
中获取数据:
String content = null;
try {
content = configService.getConfig(dataId, groupId, DEFAULT_TIMEOUT);
}
二、NacosConfigService
@Override
public String getConfig(String dataId, String group, long timeoutMs) throws NacosException {
return getConfigInner(namespace, dataId, group, timeoutMs);
}
getConfigInner
代码如下:
private String getConfigInner(String tenant, String dataId, String group, long timeoutMs) throws NacosException {
group = blank2defaultGroup(group);
ParamUtils.checkKeyParam(dataId, group);
ConfigResponse cr = new ConfigResponse();
cr.setDataId(dataId);
cr.setTenant(tenant);
cr.setGroup(group);
// We first try to use local failover content if exists.
// A config content for failover is not created by client program automatically,
// but is maintained by user.
// This is designed for certain scenario like client emergency reboot,
// changing config needed in the same time, while nacos server is down.
String content = LocalConfigInfoProcessor.getFailover(worker.getAgentName(), dataId, group, tenant);
if (content != null) {
LOGGER.warn("[{}] [get-config] get failover ok, dataId={}, group={}, tenant={}, config={}",
worker.getAgentName(), dataId, group, tenant, ContentUtils.truncateContent(content));
cr.setContent(content);
String encryptedDataKey = LocalEncryptedDataKeyProcessor
.getEncryptDataKeyFailover(agent.getName(), dataId, group, tenant);
cr.setEncryptedDataKey(encryptedDataKey);
configFilterChainManager.doFilter(null, cr);
content = cr.getContent();
return content;
}
try {
ConfigResponse response = worker.getServerConfig(dataId, group, tenant, timeoutMs, false);
cr.setContent(response.getContent());
cr.setEncryptedDataKey(response.getEncryptedDataKey());
configFilterChainManager.doFilter(null, cr);
content = cr.getContent();
return content;
} catch (NacosException ioe) {
if (NacosException.NO_RIGHT == ioe.getErrCode()) {
throw ioe;
}
LOGGER.warn("[{}] [get-config] get from server error, dataId={}, group={}, tenant={}, msg={}",
worker.getAgentName(), dataId, group, tenant, ioe.toString());
}
content = LocalConfigInfoProcessor.getSnapshot(worker.getAgentName(), dataId, group, tenant);
if (content != null) {
LOGGER.warn("[{}] [get-config] get snapshot ok, dataId={}, group={}, tenant={}, config={}",
worker.getAgentName(), dataId, group, tenant, ContentUtils.truncateContent(content));
}
cr.setContent(content);
String encryptedDataKey = LocalEncryptedDataKeyProcessor
.getEncryptDataKeySnapshot(agent.getName(), dataId, group, tenant);
cr.setEncryptedDataKey(encryptedDataKey);
configFilterChainManager.doFilter(null, cr);
content = cr.getContent();
return content;
}
以上这个方法是客户端获取config data的方法,我们可以从其中看出三个获取配置的方式:
LocalConfigInfoProcessor.getFailover
:从客户端本地一个文件中尝试获取配置,这种方式用于服务故障时临时使用。可以直接在指定地址创建文件,客户端启动时,会优先从此文件获取配置。ConfigResponse response = worker.getServerConfig(dataId, group, tenant, timeoutMs, false)
:请求Nacos服务端,获取配置数据。LocalConfigInfoProcessor.getSnapshot(worker.getAgentName(), dataId, group, tenant);
:从本地快照中获取数据。
三、ClientWorker
上文中我们看到了客户端从服务端获取数据的代码:ConfigResponse response = worker.getServerConfig(dataId, group, tenant, timeoutMs, false)
。我们接下来看看这个类是如何从服务端获取数据的。
这里可以看到是最终是发起了一个GRPC
请求去服务端
获取数据。
四、服务端处理逻辑
之前梳理服务注册时,我们了解过服务端处理GRPC
请求的入口类:GrpcRequestAcceptor
。
处理请求的主要逻辑如下:
- 从请求中获取请求类型type
- 根据type获取处理当前请求的处理类:
RequestHandler requestHandler = requestHandlerRegistry.getByRequestType(type);
- 处理请求,获取结果:
Response response = requestHandler.handleRequest(request, requestMeta);
当前请求的处理类是:ConfigQueryRequestHandler
这个类会从server端的本地缓存或者指定数据库去获取配置数据。
总结
本篇我们简单梳理了客户端和服务端在获取配置时的大致流程。可以看到客户端在启动时,优先从客户端本地去尝试获取数据,获取不到时,才会请求服务端去获取数据。