准备
源码链接:gitee.com/mirrors/Nac…
使用分支:v2.x-develop
核心类
NacosConfigService:配置中心核心类;
ConfigPublishRequestHandler:发布配置处理核心类;
ClientWorker:配置中心长轮询的核心类;
ConfigChangeBatchListenRequestHandler:服务端批量获取变更配置的处理类
ConfigQueryRequestHandler:服务端获取配置信息处理类
源码解读
- 发布配置【客户端】
- client/src/main/java/com/alibaba/nacos/client/config/NacosConfigService.java
@Override
public boolean publishConfig(String dataId, String group, String content) throws NacosException {//发布配置入口
return publishConfig(dataId, group, content, ConfigType.getDefaultType().getType());//※发布默认类型的配置(文本)
}
@Override
public boolean publishConfig(String dataId, String group, String content, String type) throws NacosException {
return publishConfigInner(namespace, dataId, group, null, null, null, content, type, null);//※发布配置核心
}
private boolean publishConfigInner(String tenant, String dataId, String group, String tag, String appName,
String betaIps, String content, String type, String casMd5) throws NacosException {
group = blank2defaultGroup(group);
ParamUtils.checkParam(dataId, group, content);
//初始化配置请求
ConfigRequest cr = new ConfigRequest();
cr.setDataId(dataId);
cr.setTenant(tenant);
cr.setGroup(group);
cr.setContent(content);
cr.setType(type);
configFilterChainManager.doFilter(cr, null);
content = cr.getContent();
String encryptedDataKey = cr.getEncryptedDataKey();
return worker
.publishConfig(dataId, group, tenant, appName, tag, betaIps, content, encryptedDataKey, casMd5, type);//※使用grpc发布配置
}
2.client/src/main/java/com/alibaba/nacos/client/config/impl/ClientWorker.java
/**
* publish config.
*
* @param dataId dataId.
* @param group group.
* @param tenant tenant.
* @param appName appName.
* @param tag tag.
* @param betaIps betaIps.
* @param content content.
* @param casMd5 casMd5.
* @param type type.
* @return success or not.
* @throws NacosException exception throw.
*/
public boolean publishConfig(String dataId, String group, String tenant, String appName, String tag, String betaIps,
String content, String encryptedDataKey, String casMd5, String type) throws NacosException {
return agent.publishConfig(dataId, group, tenant, appName, tag, betaIps, content, encryptedDataKey, casMd5,
type);
}
@Override
public boolean publishConfig(String dataId, String group, String tenant, String appName, String tag,
String betaIps, String content, String encryptedDataKey, String casMd5, String type)
throws NacosException {
try {
ConfigPublishRequest request = new ConfigPublishRequest(dataId, group, tenant, content);
request.setCasMd5(casMd5);
request.putAdditionalParam(TAG_PARAM, tag);
request.putAdditionalParam(APP_NAME_PARAM, appName);
request.putAdditionalParam(BETAIPS_PARAM, betaIps);
request.putAdditionalParam(TYPE_PARAM, type);
request.putAdditionalParam(ENCRYPTED_DATA_KEY_PARAM, encryptedDataKey == null ? "" : encryptedDataKey);
ConfigPublishResponse response = (ConfigPublishResponse) requestProxy(getOneRunningClient(), request);//※使用代理请求发送配置
if (!response.isSuccess()) {
LOGGER.warn("[{}] [publish-single] fail, dataId={}, group={}, tenant={}, code={}, msg={}",
this.getName(), dataId, group, tenant, response.getErrorCode(), response.getMessage());
return false;
} else {
LOGGER.info("[{}] [publish-single] ok, dataId={}, group={}, tenant={}", getName(),
dataId, group, tenant);
return true;
}
} catch (Exception e) {
LOGGER.warn("[{}] [publish-single] error, dataId={}, group={}, tenant={}, code={}, msg={}",
this.getName(), dataId, group, tenant, "unknown", e.getMessage());
return false;
}
}
private Response requestProxy(RpcClient rpcClientInner, Request request) throws NacosException {
return requestProxy(rpcClientInner, request, requestTimeout);
}
private Response requestProxy(RpcClient rpcClientInner, Request request, long timeoutMills)
throws NacosException {
try {
request.putAllHeader(super.getSecurityHeaders(resourceBuild(request)));
request.putAllHeader(super.getCommonHeader());
} catch (Exception e) {
throw new NacosException(NacosException.CLIENT_INVALID_PARAM, e);
}
JsonObject asJsonObjectTemp = new Gson().toJsonTree(request).getAsJsonObject();
asJsonObjectTemp.remove("headers");
asJsonObjectTemp.remove("requestId");
boolean limit = Limiter.isLimit(request.getClass() + asJsonObjectTemp.toString());
if (limit) {
throw new NacosException(NacosException.CLIENT_OVER_THRESHOLD,
"More than client-side current limit threshold");
}
Response response;
if (timeoutMills < 0) {
response = rpcClientInner.request(request);//※将配置发送到服务端
} else {
response = rpcClientInner.request(request, timeoutMills);//※将配置发送到服务端
}
// If the 403 login operation is triggered, refresh the accessToken of the client
if (response.getErrorCode() == ConfigQueryResponse.NO_RIGHT) {
reLogin();
}
return response;
}
- 发布配置【服务端】 1.config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigPublishRequestHandler.java
@Override
@TpsControl(pointName = "ConfigPublish")
@Secured(action = ActionTypes.WRITE, signType = SignType.CONFIG)
@ExtractorManager.Extractor(rpcExtractor = ConfigRequestParamExtractor.class)
public ConfigPublishResponse handle(ConfigPublishRequest request, RequestMeta meta) throws NacosException {
try {
String dataId = request.getDataId();
String group = request.getGroup();
String content = request.getContent();
final String tenant = NamespaceUtil.processNamespaceParameter(request.getTenant());
final String srcIp = meta.getClientIp();
final String tag = request.getAdditionParam("tag");
final String appName = request.getAdditionParam("appName");
final String type = request.getAdditionParam("type");
final String srcUser = request.getAdditionParam("src_user");
final String encryptedDataKey = request.getAdditionParam("encryptedDataKey");
// check tenant
ParamUtils.checkParam(dataId, group, "datumId", content);
ParamUtils.checkParam(tag);
ConfigForm configForm = new ConfigForm();
configForm.setDataId(dataId);
configForm.setGroup(group);
configForm.setNamespaceId(tenant);
configForm.setContent(content);
configForm.setTag(tag);
configForm.setAppName(appName);
configForm.setSrcUser(srcUser);
configForm.setConfigTags(request.getAdditionParam("config_tags"));
configForm.setDesc(request.getAdditionParam("desc"));
configForm.setUse(request.getAdditionParam("use"));
configForm.setEffect(request.getAdditionParam("effect"));
configForm.setType(type);
configForm.setSchema(request.getAdditionParam("schema"));
if (!ConfigType.isValidType(type)) {
configForm.setType(ConfigType.getDefaultType().getType());
}
ConfigRequestInfo configRequestInfo = new ConfigRequestInfo();
configRequestInfo.setSrcIp(srcIp);
configRequestInfo.setSrcType(RPC);
configRequestInfo.setRequestIpApp(meta.getLabels().get(Constants.APPNAME));
configRequestInfo.setBetaIps(request.getAdditionParam("betaIps"));
configRequestInfo.setCasMd5(request.getCasMd5());
String encryptedDataKeyFinal = null;
if (StringUtils.isNotBlank(encryptedDataKey)) {
encryptedDataKeyFinal = encryptedDataKey;
} else {
Pair<String, String> pair = EncryptionHandler.encryptHandler(dataId, content);
content = pair.getSecond();
encryptedDataKeyFinal = pair.getFirst();
configForm.setContent(content);
}
try {
configOperationService.publishConfig(configForm, configRequestInfo, encryptedDataKeyFinal);//※将处理好的配置信息发布到服务端
return ConfigPublishResponse.buildSuccessResponse();
} catch (NacosApiException | ConfigAlreadyExistsException ex) {
return ConfigPublishResponse.buildFailResponse(ResponseCode.FAIL.getCode(),
ex.getErrMsg());
}
} catch (Exception e) {
Loggers.REMOTE_DIGEST.error("[ConfigPublishRequestHandler] publish config error ,request ={}", request, e);
return ConfigPublishResponse.buildFailResponse(
(e instanceof NacosException) ? ((NacosException) e).getErrCode() : ResponseCode.FAIL.getCode(),
e.getMessage());
}
}
- config/src/main/java/com/alibaba/nacos/config/server/service/ConfigOperationService.java
/**
* Adds or updates non-aggregated data.
*
* @throws NacosException NacosException.
*/
public Boolean publishConfig(ConfigForm configForm, ConfigRequestInfo configRequestInfo, String encryptedDataKey) throws NacosException {
Map<String, Object> configAdvanceInfo = getConfigAdvanceInfo(configForm);
ParamUtils.checkParam(configAdvanceInfo);
configForm.setEncryptedDataKey(encryptedDataKey);
ConfigInfo configInfo = new ConfigInfo(configForm.getDataId(), configForm.getGroup(),
configForm.getNamespaceId(), configForm.getAppName(), configForm.getContent());
//set old md5
if (StringUtils.isNotBlank(configRequestInfo.getCasMd5())) {
configInfo.setMd5(configRequestInfo.getCasMd5());
}
configInfo.setType(configForm.getType());
configInfo.setEncryptedDataKey(encryptedDataKey);
ConfigOperateResult configOperateResult;
//beta publish
if (StringUtils.isNotBlank(configRequestInfo.getBetaIps())) {//※发布到beta表:config_info_beta
configForm.setGrayName(BetaGrayRule.TYPE_BETA);
configForm.setGrayRuleExp(configRequestInfo.getBetaIps());
configForm.setGrayVersion(BetaGrayRule.VERSION);
configGrayModelMigrateService.persistBeta(configForm, configInfo, configRequestInfo);//※使用jdbc持久化到默认数据库或第三方数据库【mysql】
configForm.setGrayPriority(Integer.MAX_VALUE);
publishConfigGray(BetaGrayRule.TYPE_BETA, configForm, configRequestInfo);
return Boolean.TRUE;
}
// tag publish
if (StringUtils.isNotBlank(configForm.getTag())) {//※发布到tag表:config_info_tag
configForm.setGrayName(TagGrayRule.TYPE_TAG + "_" + configForm.getTag());
configForm.setGrayRuleExp(configForm.getTag());
configForm.setGrayVersion(TagGrayRule.VERSION);
configForm.setGrayPriority(Integer.MAX_VALUE - 1);
configGrayModelMigrateService.persistTagv1(configForm, configInfo, configRequestInfo);//※使用jdbc持久化到默认数据库或第三方数据库【mysql】
publishConfigGray(TagGrayRule.TYPE_TAG, configForm, configRequestInfo);
return Boolean.TRUE;
}
//formal publish
if (StringUtils.isNotBlank(configRequestInfo.getCasMd5())) {//※发布到formal表:config_info【两种方式,一种使用使用版本号做乐观锁,一种无乐观锁】
configOperateResult = configInfoPersistService.insertOrUpdateCas(configRequestInfo.getSrcIp(),
configForm.getSrcUser(), configInfo, configAdvanceInfo);//※使用jdbc持久化到默认数据库或第三方数据库【mysql】
if (!configOperateResult.isSuccess()) {
LOGGER.warn(
"[cas-publish-config-fail] srcIp = {}, dataId= {}, casMd5 = {}, msg = server md5 may have changed.",
configRequestInfo.getSrcIp(), configForm.getDataId(), configRequestInfo.getCasMd5());
throw new NacosApiException(HttpStatus.INTERNAL_SERVER_ERROR.value(), ErrorCode.RESOURCE_CONFLICT,
"Cas publish fail, server md5 may have changed.");
}
} else {
if (configRequestInfo.getUpdateForExist()) {
configOperateResult = configInfoPersistService.insertOrUpdate(configRequestInfo.getSrcIp(),
configForm.getSrcUser(), configInfo, configAdvanceInfo);//※使用jdbc持久化到默认数据库或第三方数据库【mysql】
} else {
try {
configOperateResult = configInfoPersistService.addConfigInfo(configRequestInfo.getSrcIp(),
configForm.getSrcUser(), configInfo, configAdvanceInfo);//※使用jdbc持久化到默认数据库或第三方数据库【mysql】
} catch (DataIntegrityViolationException ive) {
LOGGER.warn("[publish-config-failed] config already exists. dataId: {}, group: {}, namespaceId: {}",
configForm.getDataId(), configForm.getGroup(), configForm.getNamespaceId());
throw new ConfigAlreadyExistsException(
String.format("config already exist, dataId: %s, group: %s, namespaceId: %s",
configForm.getDataId(), configForm.getGroup(), configForm.getNamespaceId()));
}
}
}
ConfigChangePublisher.notifyConfigChange(
new ConfigDataChangeEvent(configForm.getDataId(), configForm.getGroup(), configForm.getNamespaceId(),
configOperateResult.getLastModified()));
ConfigTraceService.logPersistenceEvent(configForm.getDataId(), configForm.getGroup(),
configForm.getNamespaceId(), configRequestInfo.getRequestIpApp(), configOperateResult.getLastModified(),
InetUtils.getSelfIP(), ConfigTraceService.PERSISTENCE_EVENT, ConfigTraceService.PERSISTENCE_TYPE_PUB,
configForm.getContent());
return true;
}
- 监听配置【客户端】
- client/src/main/java/com/alibaba/nacos/client/config/impl/ClientWorker.java
public ClientWorker(final ConfigFilterChainManager configFilterChainManager, ConfigServerListManager serverListManager,
final NacosClientProperties properties) throws NacosException {
this.configFilterChainManager = configFilterChainManager;
init(properties);
agent = new ConfigRpcTransportClient(properties, serverListManager);
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(initWorkerThreadCount(properties),
new NameThreadFactory("com.alibaba.nacos.client.Worker"));
agent.setExecutor(executorService);
agent.start();//※ 客户端代理开启
}
- client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigTransportClient.java
/**
* base start client.
*/
public void start() throws NacosException {
securityProxy.login(this.properties);
this.executor.scheduleWithFixedDelay(() -> securityProxy.login(properties), 0,
this.securityInfoRefreshIntervalMills, TimeUnit.MILLISECONDS);
startInternal();//※ 开始轮询
}
- client/src/main/java/com/alibaba/nacos/client/config/impl/ClientWorker.java
@Override
public void startInternal() {
executor.schedule(() -> {
while (!executor.isShutdown() && !executor.isTerminated()) {
try {
listenExecutebell.poll(5L, TimeUnit.SECONDS);
if (executor.isShutdown() || executor.isTerminated()) {
continue;
}
executeConfigListen();//※ 执行配置监听
} catch (Throwable e) {
LOGGER.error("[rpc listen execute] [rpc listen] exception", e);
try {
Thread.sleep(50L);
} catch (InterruptedException interruptedException) {
//ignore
}
notifyListenConfig();
}
}
}, 0L, TimeUnit.MILLISECONDS);
}
@Override
public void executeConfigListen() throws NacosException {
Map<String, List<CacheData>> listenCachesMap = new HashMap<>(16);
Map<String, List<CacheData>> removeListenCachesMap = new HashMap<>(16);
long now = System.currentTimeMillis();
boolean needAllSync = now - lastAllSyncTime >= ALL_SYNC_INTERNAL;
for (CacheData cache : cacheMap.get().values()) {
synchronized (cache) {
checkLocalConfig(cache);
// check local listeners consistent.
if (cache.isConsistentWithServer()) {
cache.checkListenerMd5();
if (!needAllSync) {
continue;
}
}
// If local configuration information is used, then skip the processing directly.
if (cache.isUseLocalConfigInfo()) {
continue;
}
if (!cache.isDiscard()) {
List<CacheData> cacheDatas = listenCachesMap.computeIfAbsent(String.valueOf(cache.getTaskId()),
k -> new LinkedList<>());
cacheDatas.add(cache);
} else {
List<CacheData> cacheDatas = removeListenCachesMap.computeIfAbsent(
String.valueOf(cache.getTaskId()), k -> new LinkedList<>());
cacheDatas.add(cache);
}
}
}
//execute check listen ,return true if has change keys.
boolean hasChangedKeys = checkListenCache(listenCachesMap);//※检查是否发生变化
//execute check remove listen.
checkRemoveListenCache(removeListenCachesMap);
if (needAllSync) {
lastAllSyncTime = now;
}
//If has changed keys,notify re sync md5.
if (hasChangedKeys) {
notifyListenConfig();
}
}
private boolean checkListenCache(Map<String, List<CacheData>> listenCachesMap) throws NacosException {
final AtomicBoolean hasChangedKeys = new AtomicBoolean(false);
if (!listenCachesMap.isEmpty()) {
List<Future> listenFutures = new ArrayList<>();
for (Map.Entry<String, List<CacheData>> entry : listenCachesMap.entrySet()) {
String taskId = entry.getKey();
//获取客户端
RpcClient rpcClient = ensureRpcClient(taskId);
ExecutorService executorService = ensureSyncExecutor(taskId);
Future future = executorService.submit(() -> {
List<CacheData> listenCaches = entry.getValue();
//reset notify change flag.
for (CacheData cacheData : listenCaches) {
cacheData.getReceiveNotifyChanged().set(false);
}
//rpc请求:配置变化监听
ConfigBatchListenRequest configChangeListenRequest = buildConfigRequest(listenCaches);
configChangeListenRequest.setListen(true);
try {
ConfigChangeBatchListenResponse listenResponse = (ConfigChangeBatchListenResponse) requestProxy(
rpcClient, configChangeListenRequest);
if (listenResponse != null && listenResponse.isSuccess()) {
Set<String> changeKeys = new HashSet<String>();
//变更的配置:没有变更后的信息
List<ConfigChangeBatchListenResponse.ConfigContext> changedConfigs = listenResponse.getChangedConfigs();
//handle changed keys,notify listener
if (!CollectionUtils.isEmpty(changedConfigs)) {
hasChangedKeys.set(true);
for (ConfigChangeBatchListenResponse.ConfigContext changeConfig : changedConfigs) {
String changeKey = GroupKey.getKeyTenant(changeConfig.getDataId(),
changeConfig.getGroup(), changeConfig.getTenant());
changeKeys.add(changeKey);
boolean isInitializing = cacheMap.get().get(changeKey).isInitializing();
//※配置发生变化,刷新配置内容
refreshContentAndCheck(rpcClient, changeKey, !isInitializing);
}
}
for (CacheData cacheData : listenCaches) {
if (cacheData.getReceiveNotifyChanged().get()) {
String changeKey = GroupKey.getKeyTenant(cacheData.dataId, cacheData.group,
cacheData.getTenant());
if (!changeKeys.contains(changeKey)) {
boolean isInitializing = cacheMap.get().get(changeKey).isInitializing();
refreshContentAndCheck(rpcClient, changeKey, !isInitializing);
}
}
}
//handler content configs
for (CacheData cacheData : listenCaches) {
cacheData.setInitializing(false);
String groupKey = GroupKey.getKeyTenant(cacheData.dataId, cacheData.group,
cacheData.getTenant());
if (!changeKeys.contains(groupKey)) {
synchronized (cacheData) {
if (!cacheData.getReceiveNotifyChanged().get()) {
cacheData.setConsistentWithServer(true);
}
}
}
}
}
} catch (Throwable e) {
LOGGER.error("Execute listen config change error ", e);
try {
Thread.sleep(50L);
} catch (InterruptedException interruptedException) {
//ignore
}
notifyListenConfig();
}
});
listenFutures.add(future);
}
for (Future future : listenFutures) {
try {
future.get();
} catch (Throwable throwable) {
LOGGER.error("Async listen config change error ", throwable);
}
}
}
return hasChangedKeys.get();
}
private void refreshContentAndCheck(RpcClient rpcClient, String groupKey, boolean notify) {
if (cacheMap.get() != null && cacheMap.get().containsKey(groupKey)) {
CacheData cache = cacheMap.get().get(groupKey);
refreshContentAndCheck(rpcClient, cache, notify);//※更新本地缓存中的配置内容
}
}
private void refreshContentAndCheck(RpcClient rpcClient, CacheData cacheData, boolean notify) {
try {
ConfigResponse response = this.queryConfigInner(rpcClient, cacheData.dataId, cacheData.group,
cacheData.tenant, requestTimeout, notify);//※rpc获取最新配置内容
cacheData.setEncryptedDataKey(response.getEncryptedDataKey());
cacheData.setContent(response.getContent());
if (null != response.getConfigType()) {
cacheData.setType(response.getConfigType());
}
if (notify) {
LOGGER.info("[{}] [data-received] dataId={}, group={}, tenant={}, md5={}, type={}", agent.getName(),
cacheData.dataId, cacheData.group, cacheData.tenant, cacheData.getMd5(),
response.getConfigType());
}
cacheData.checkListenerMd5();
} catch (Exception e) {
LOGGER.error("refresh content and check md5 fail ,dataId={},group={},tenant={} ", cacheData.dataId,
cacheData.group, cacheData.tenant, e);
}
}
ConfigResponse queryConfigInner(RpcClient rpcClient, String dataId, String group, String tenant,
long readTimeouts, boolean notify) throws NacosException {
ConfigQueryRequest request = ConfigQueryRequest.build(dataId, group, tenant);
request.putHeader(NOTIFY_HEADER, String.valueOf(notify));
ConfigQueryResponse response = (ConfigQueryResponse) requestProxy(rpcClient, request, readTimeouts);
ConfigResponse configResponse = new ConfigResponse();
if (response.isSuccess()) {
LocalConfigInfoProcessor.saveSnapshot(this.getName(), dataId, group, tenant, response.getContent());
configResponse.setContent(response.getContent());
String configType;
if (StringUtils.isNotBlank(response.getContentType())) {
configType = response.getContentType();
} else {
configType = ConfigType.TEXT.getType();
}
configResponse.setConfigType(configType);
String encryptedDataKey = response.getEncryptedDataKey();
LocalEncryptedDataKeyProcessor.saveEncryptDataKeySnapshot(agent.getName(), dataId, group, tenant,
encryptedDataKey);
configResponse.setEncryptedDataKey(encryptedDataKey);
return configResponse;
} else if (response.getErrorCode() == ConfigQueryResponse.CONFIG_NOT_FOUND) {
LocalConfigInfoProcessor.saveSnapshot(this.getName(), dataId, group, tenant, null);
LocalEncryptedDataKeyProcessor.saveEncryptDataKeySnapshot(agent.getName(), dataId, group, tenant, null);
return configResponse;
} else if (response.getErrorCode() == ConfigQueryResponse.CONFIG_QUERY_CONFLICT) {
LOGGER.error(
"[{}] [sub-server-error] get server config being modified concurrently, dataId={}, group={}, "
+ "tenant={}", this.getName(), dataId, group, tenant);
throw new NacosException(NacosException.CONFLICT,
"data being modified, dataId=" + dataId + ",group=" + group + ",tenant=" + tenant);
} else {
LOGGER.error("[{}] [sub-server-error] dataId={}, group={}, tenant={}, code={}", this.getName(), dataId,
group, tenant, response);
throw new NacosException(response.getErrorCode(),
"http error, code=" + response.getErrorCode() + ",msg=" + response.getMessage() + ",dataId="
+ dataId + ",group=" + group + ",tenant=" + tenant);
}
}
- 监听配置【服务端】
- config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigChangeBatchListenRequestHandler.java
@Override
@TpsControl(pointName = "ConfigListen")
@Secured(action = ActionTypes.READ, signType = SignType.CONFIG)
@ExtractorManager.Extractor(rpcExtractor = ConfigBatchListenRequestParamExtractor.class)
public ConfigChangeBatchListenResponse handle(ConfigBatchListenRequest configChangeListenRequest, RequestMeta meta)
throws NacosException {
String connectionId = StringPool.get(meta.getConnectionId());
String tag = configChangeListenRequest.getHeader(Constants.VIPSERVER_TAG);
ParamUtils.checkParam(tag);
ConfigChangeBatchListenResponse configChangeBatchListenResponse = new ConfigChangeBatchListenResponse();
for (ConfigBatchListenRequest.ConfigListenContext listenContext : configChangeListenRequest.getConfigListenContexts()) {
String groupKey = GroupKey2.getKey(listenContext.getDataId(), listenContext.getGroup(),
listenContext.getTenant());
groupKey = StringPool.get(groupKey);
String md5 = StringPool.get(listenContext.getMd5());
if (configChangeListenRequest.isListen()) {
configChangeListenContext.addListen(groupKey, md5, connectionId);
boolean isUptoDate = ConfigCacheService.isUptodate(groupKey, md5, meta.getClientIp(), tag,
meta.getAppLabels());
if (!isUptoDate) {
//※判断服务端的md5与本地的是否一样,不一样则发生变化
configChangeBatchListenResponse.addChangeConfig(listenContext.getDataId(), listenContext.getGroup(),
listenContext.getTenant());
}
} else {
configChangeListenContext.removeListen(groupKey, connectionId);
}
}
return configChangeBatchListenResponse;
}
- config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigQueryRequestHandler.java
@Override
@TpsControl(pointName = "ConfigQuery")
@Secured(action = ActionTypes.READ, signType = SignType.CONFIG)
@ExtractorManager.Extractor(rpcExtractor = ConfigRequestParamExtractor.class)
public ConfigQueryResponse handle(ConfigQueryRequest request, RequestMeta meta) throws NacosException {
try {
request.setTenant(NamespaceUtil.processNamespaceParameter(request.getTenant()));
String dataId = request.getDataId();
String group = request.getGroup();
String tenant = request.getTenant();
String groupKey = GroupKey2.getKey(dataId, group, tenant);
boolean notify = request.isNotify();
String requestIpApp = meta.getLabels().get(CLIENT_APPNAME_HEADER);
String clientIp = meta.getClientIp();
ConfigQueryChainRequest chainRequest = ConfigChainRequestExtractorService.getExtractor().extract(request, meta);
ConfigQueryChainResponse chainResponse = configQueryChainService.handle(chainRequest);
if (ResponseCode.FAIL.getCode() == chainResponse.getResultCode()) {
return ConfigQueryResponse.buildFailResponse(ResponseCode.FAIL.getCode(), chainResponse.getMessage());//※ 找不到配置
}
if (chainResponse.getStatus() == ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_NOT_FOUND) {
return handlerConfigNotFound(request.getDataId(), request.getGroup(), request.getTenant(), requestIpApp, clientIp, notify);//※配置存在冲突
}
if (chainResponse.getStatus() == ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_QUERY_CONFLICT) {
return handlerConfigConflict(clientIp, groupKey);
}
ConfigQueryResponse response = new ConfigQueryResponse();//※ 封装配置信息
// Check if there is a matched gray rule
if (chainResponse.getStatus() == ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_GRAY) {
if (BetaGrayRule.TYPE_BETA.equals(chainResponse.getMatchedGray().getGrayRule().getType())) {
response.setBeta(true);
} else if (TagGrayRule.TYPE_TAG.equals(chainResponse.getMatchedGray().getGrayRule().getType())) {
response.setTag(URLEncoder.encode(chainResponse.getMatchedGray().getRawGrayRule(), ENCODE_UTF8));
}
}
// Check if there is a special tag
if (chainResponse.getStatus() == ConfigQueryChainResponse.ConfigQueryStatus.SPECIAL_TAG_CONFIG_NOT_FOUND) {
response.setTag(request.getTag());
}
response.setMd5(chainResponse.getMd5());
response.setEncryptedDataKey(chainResponse.getEncryptedDataKey());
response.setContent(chainResponse.getContent());
response.setContentType(chainResponse.getConfigType());
response.setLastModified(chainResponse.getLastModified());
String pullType = ConfigTraceService.PULL_TYPE_OK;
if (chainResponse.getContent() == null) {
pullType = ConfigTraceService.PULL_TYPE_NOTFOUND;
response.setErrorInfo(ConfigQueryResponse.CONFIG_NOT_FOUND, "config data not exist");
} else {
response.setResultCode(ResponseCode.SUCCESS.getCode());
}
String pullEvent = resolvePullEventType(chainResponse, request.getTag());
LogUtil.PULL_CHECK_LOG.warn("{}|{}|{}|{}", groupKey, clientIp, response.getMd5(), TimeUtils.getCurrentTimeStr());
final long delayed = System.currentTimeMillis() - response.getLastModified();
ConfigTraceService.logPullEvent(dataId, group, tenant, requestIpApp, response.getLastModified(), pullEvent, pullType,
delayed, clientIp, notify, "grpc");
return response;
} catch (Exception e) {
LOGGER.error("Failed to handle grpc configuration query", e);
return ConfigQueryResponse.buildFailResponse(ResponseCode.FAIL.getCode(), e.getMessage());
}
}
- 获取配置【客户端】
- api/src/main/java/com/alibaba/nacos/api/config/ConfigService.java
/**
* Get config.
*
* @param dataId dataId
* @param group group
* @param timeoutMs read timeout
* @return config value
* @throws NacosException NacosException
*/
String getConfig(String dataId, String group, long timeoutMs) throws NacosException;
- client/src/main/java/com/alibaba/nacos/client/config/NacosConfigService.java
@Override
public String getConfig(String dataId, String group, long timeoutMs) throws NacosException {
return getConfigInner(namespace, dataId, group, timeoutMs);
}
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={}",
worker.getAgentName(), dataId, group, tenant);
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);//※通过grpc获取配置
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={}",
worker.getAgentName(), dataId, group, tenant);
}
cr.setContent(content);
String encryptedDataKey = LocalEncryptedDataKeyProcessor
.getEncryptDataKeySnapshot(agent.getName(), dataId, group, tenant);
cr.setEncryptedDataKey(encryptedDataKey);
configFilterChainManager.doFilter(null, cr);
content = cr.getContent();
return content;
}
- 获取配置【服务端】
与监听配置【服务端】一样
一句话概括
通过grpc将配置信息发布到服务端。客户端轮询配置是否发生变化【每次查询都是批量的】,获取到变化配置后,单个获取具体的配置内容更新到客户端缓存。
一天一捏捏,接着扒拉~~~