Cloud Native 15 min read

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.

Ops Development Stories
Ops Development Stories
Ops Development Stories
How Nacos Retrieves and Syncs Configurations: A Deep Dive into the Source Code

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:8848

Client 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);
    }
}
NacosPropertySourceLocator

implements 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#getConfigInner

first 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

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

javaMicroservicesNacosSpring Cloud
Ops Development Stories
Written by

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.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.