How Nacos Retrieves and Syncs Configurations: A Deep Dive into the Source Code
This article walks through the inner workings of Nacos Config Center, detailing environment setup, dependency inclusion, client initialization, configuration loading, dynamic synchronization mechanisms, long‑polling processes, and server‑side handling, all illustrated with full source‑code snippets and explanations.
This article analyzes the Nacos configuration center from a source‑code perspective, covering environment preparation, dependency configuration, client initialization, configuration loading, dynamic synchronization, long‑polling, and server response handling.
Environment and Dependencies
JDK 1.8
nacos-server-1.4.2
spring-boot-2.3.5.RELEASE
spring-cloud-Hoxton.SR8
spring-cloud-alibaba-2.2.5.RELEASE
To use Nacos as a configuration center, add the following Maven dependency:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>Configure the Nacos server address in bootstrap.yml:
spring:
cloud:
nacos:
config:
server-addr: 127.0.0.1:8848Client Initialization
The client is initialized via NacosConfigBootstrapConfiguration, which creates NacosConfigManager and NacosPropertySourceLocator beans.
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(name = "spring.cloud.nacos.config.enabled", matchIfMissing = true)
public class NacosConfigBootstrapConfiguration {
@Bean
@ConditionalOnMissingBean
public NacosConfigManager nacosConfigManager(NacosConfigProperties nacosConfigProperties) {
return new NacosConfigManager(nacosConfigProperties);
}
@Bean
public NacosPropertySourceLocator nacosPropertySourceLocator(NacosConfigManager nacosConfigManager) {
return new NacosPropertySourceLocator(nacosConfigManager);
}
// ...
}During construction, NacosConfigManager calls createConfigService to instantiate com.alibaba.nacos.client.config.NacosConfigService via reflection.
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);
}
} NacosPropertySourceLocatorimplements PropertySourceLocator and loads configuration via its locate method.
@Override
public PropertySource<?> locate(Environment env) {
nacosConfigProperties.setEnvironment(env);
ConfigService configService = nacosConfigManager.getConfigService();
if (configService == null) {
log.warn("no instance of config service found, can't load config from nacos");
return null;
}
long timeout = nacosConfigProperties.getTimeout();
nacosPropertySourceBuilder = new NacosPropertySourceBuilder(configService, timeout);
// build CompositePropertySource with shared, extended and application configs
// ...
return composite;
}Configuration Loading Process
The loading involves three methods: loadSharedConfiguration, loadExtConfiguration, and loadApplicationConfiguration. The latter loads configuration files based on dataId, group, and fileExtension.
private void loadApplicationConfiguration(CompositePropertySource compositePropertySource, String dataIdPrefix,
NacosConfigProperties properties, Environment environment) {
String fileExtension = properties.getFileExtension();
String nacosGroup = properties.getGroup();
loadNacosDataIfPresent(compositePropertySource, dataIdPrefix, nacosGroup, fileExtension, true);
loadNacosDataIfPresent(compositePropertySource, dataIdPrefix + "." + fileExtension, nacosGroup, fileExtension, true);
for (String profile : environment.getActiveProfiles()) {
String dataId = dataIdPrefix + "-" + profile + "." + fileExtension;
loadNacosDataIfPresent(compositePropertySource, dataId, nacosGroup, fileExtension, true);
}
}The core method loadNacosDataIfPresent fetches the configuration from Nacos and adds it to the composite source.
private void loadNacosDataIfPresent(final CompositePropertySource composite,
final String dataId, final String group, String fileExtension, boolean isRefreshable) {
if (dataId == null || dataId.trim().length() < 1) return;
if (group == null || group.trim().length() < 1) return;
NacosPropertySource propertySource = this.loadNacosPropertySource(dataId, group, fileExtension, isRefreshable);
this.addFirstPropertySource(composite, propertySource, false);
}Remote Configuration Retrieval
NacosConfigService#getConfigInnerfirst checks local failover, then contacts the server if needed.
private String getConfigInner(String tenant, String dataId, String group, long timeoutMs) throws NacosException {
group = null2defaultGroup(group);
ParamUtils.checkKeyParam(dataId, group);
ConfigResponse cr = new ConfigResponse();
cr.setDataId(dataId);
cr.setTenant(tenant);
cr.setGroup(group);
String content = LocalConfigInfoProcessor.getFailover(agent.getName(), dataId, group, tenant);
if (content != null) {
LOGGER.warn("[{}] [get-config] get failover ok, dataId={}, group={}, tenant={}, config={}",
agent.getName(), dataId, group, tenant, ContentUtils.truncateContent(content));
cr.setContent(content);
configFilterChainManager.doFilter(null, cr);
return cr.getContent();
}
try {
String[] ct = worker.getServerConfig(dataId, group, tenant, timeoutMs);
cr.setContent(ct[0]);
configFilterChainManager.doFilter(null, cr);
return cr.getContent();
} catch (NacosException ioe) {
// handle errors and fallback to snapshot
// ...
throw ioe;
}
}The ClientWorker performs long‑polling to detect changes.
public ClientWorker(final HttpAgent agent, final ConfigFilterChainManager configFilterChainManager,
final Properties properties) {
this.agent = agent;
this.configFilterChainManager = configFilterChainManager;
init(properties);
this.executor = Executors.newScheduledThreadPool(1, r -> {
Thread t = new Thread(r);
t.setName("com.alibaba.nacos.client.Worker." + agent.getName());
t.setDaemon(true);
return t;
});
this.executorService = Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors(), r -> {
Thread t = new Thread(r);
t.setName("com.alibaba.nacos.client.Worker.longPolling." + agent.getName());
t.setDaemon(true);
return t;
});
this.executor.scheduleWithFixedDelay(() -> {
try { checkConfigInfo(); } catch (Throwable e) { LOGGER.error("[" + agent.getName() + "] [sub-check] rotate check error", e); }
}, 1L, 10L, TimeUnit.MILLISECONDS);
}The checkConfigInfo method distributes tasks, checks local cache, and triggers remote updates via LongPollingRunnable.
public void checkConfigInfo() {
int listenerSize = cacheMap.size();
int longingTaskCount = (int) Math.ceil(listenerSize / ParamUtil.getPerTaskConfigSize());
if (longingTaskCount > currentLongingTaskCount) {
for (int i = (int) currentLongingTaskCount; i < longingTaskCount; i++) {
executorService.execute(new LongPollingRunnable(i));
}
currentLongingTaskCount = longingTaskCount;
}
}Long‑Polling Execution
The LongPollingRunnable checks local configurations, queries the server for updates, and notifies listeners when the MD5 hash changes.
public void run() {
List<CacheData> cacheDatas = new ArrayList<>();
// check local config and trigger listener if needed
// query server for changed dataIds
// update cache and notify listeners
// reschedule itself for continuous polling
}Server‑Side Listener Endpoint
The server holds the request and responds when configuration changes are detected.
@PostMapping("/listener")
@Secured(action = ActionTypes.READ, parser = ConfigResourceParser.class)
public void listener(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", true);
String probeModify = request.getParameter("Listening-Configs");
if (StringUtils.isBlank(probeModify)) {
throw new IllegalArgumentException("invalid probeModify");
}
probeModify = URLDecoder.decode(probeModify, Constants.ENCODE);
Map<String, String> clientMd5Map = MD5Util.getClientMd5Map(probeModify);
inner.doPollingConfig(request, response, clientMd5Map, probeModify.length());
}Long‑Polling Service Core
The LongPollingService adds clients to a waiting list, calculates timeout, and either returns immediate changes or holds the request for long‑polling.
public void addLongPollingClient(HttpServletRequest req, HttpServletResponse rsp, Map<String, String> clientMd5Map, int probeRequestSize) {
String timeoutHeader = req.getHeader(LongPollingService.LONG_POLLING_HEADER);
long timeout = Math.max(10000, Long.parseLong(timeoutHeader) - SwitchService.getSwitchInteger(SwitchService.FIXED_DELAY_TIME, 500));
AsyncContext asyncContext = req.startAsync();
asyncContext.setTimeout(0L);
ConfigExecutor.executeLongPolling(new ClientLongPolling(asyncContext, clientMd5Map, ip, probeRequestSize, timeout, appName, tag));
}Diagram
The following diagram illustrates the Nacos configuration synchronization process:
References
https://blog.csdn.net/jason_jiahongfei/article/details/108373442
https://www.cnblogs.com/lockedsher/articles/14447700.html
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Ops Development Stories
Maintained by a like‑minded team, covering both operations and development. Topics span Linux ops, DevOps toolchain, Kubernetes containerization, monitoring, log collection, network security, and Python or Go development. Team members: Qiao Ke, wanger, Dong Ge, Su Xin, Hua Zai, Zheng Ge, Teacher Xia.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
