Cloud Native 17 min read

How Nacos Registers Services: Deep Dive into AP/CP Modes and Source Code

This article dissects Nacos's service registration mechanism from both client and server perspectives, explaining how it supports both AP and CP consistency models, the core classes involved, and practical debugging steps for developers working with Spring Cloud Alibaba.

Ops Development Stories
Ops Development Stories
Ops Development Stories
How Nacos Registers Services: Deep Dive into AP/CP Modes and Source Code

In this article we analyze Nacos's service registration process from a source‑code perspective, covering both the server side and the client side that uses

spring-cloud-alibaba

as the core client component.

Environment

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

Nacos Architecture

When built on Spring Boot, Nacos sits in the service architecture as shown below.

Middlewares with similar functionality include Eureka, Zookeeper, Consul, and Etcd. Nacos’s unique feature is its ability to support both AP and CP modes, using the Raft protocol for strong consistency.

Nacos Client

Service Registration Client

Adding Dependency

Import the dependency:

<code>&lt;dependency&gt;
  &lt;groupId&gt;com.alibaba.cloud&lt;/groupId&gt;
  &lt;artifactId&gt;spring-cloud-starter-alibaba-nacos-discovery&lt;/artifactId&gt;
&lt;/dependency&gt;</code>

The client initiates registration via Spring events. The core auto‑configuration class is

NacosServiceRegistryAutoConfiguration

, which defines three beans:

NacosServiceRegistry
NacosRegistration
NacosAutoServiceRegistration

NacosAutoServiceRegistration

This class extends

AbstractAutoServiceRegistration

and implements

ApplicationContextAware

and

ApplicationListener&lt;WebServerInitializedEvent&gt;

. After the container starts,

onApplicationEvent

triggers

bind(event)

, which eventually calls

start()

and then

register()

to invoke

serviceRegistry.register()

.

<code>public void onApplicationEvent(WebServerInitializedEvent event) {
    bind(event);
}

public void bind(WebServerInitializedEvent event) {
    // omitted context checks
    this.port.compareAndSet(0, event.getWebServer().getPort());
    this.start();
}

public void start() {
    if (!isEnabled()) {
        return;
    }
    if (!this.running.get()) {
        this.context.publishEvent(new InstancePreRegisteredEvent(this, getRegistration()));
        register();
        if (shouldRegisterManagement()) {
            registerManagement();
        }
        this.context.publishEvent(new InstanceRegisteredEvent<>(this, getConfiguration()));
        this.running.compareAndSet(false, true);
    }
}

private void register() {
    this.serviceRegistry.register(getRegistration());
}</code>

NacosServiceRegistry

The

register

method creates a

NamingService

instance, builds an

Instance

from the registration data, and calls

namingService.registerInstance

.

<code>public void register(Registration registration) {
    if (StringUtils.isEmpty(registration.getServiceId())) {
        log.warn("No service to register for nacos client...");
        return;
    }
    NamingService namingService = namingService();
    String serviceId = registration.getServiceId();
    String group = nacosDiscoveryProperties.getGroup();
    Instance instance = getNacosInstanceFromRegistration(registration);
    try {
        namingService.registerInstance(serviceId, group, instance);
        log.info("nacos registry, {} {} {}:{} register finished", group, serviceId,
                instance.getIp(), instance.getPort());
    } catch (Exception e) {
        log.error("nacos registry, {} register failed...{},", serviceId,
                registration.toString(), e);
        rethrowRuntimeException(e);
    }
}</code>

Service Registration on the Server

Nacos can operate in both AP and CP modes. The server stores instances via

InstanceController#register

, which parses the request and calls

serviceManager.registerInstance

. The underlying

ConsistencyService

decides which consistency protocol to use.

<code>@CanDistro
@PostMapping
@Secured(parser = NamingResourceParser.class, action = ActionTypes.WRITE)
public String register(HttpServletRequest request) throws Exception {
    String namespaceId = WebUtils.optional(request, CommonParams.NAMESPACE_ID, Constants.DEFAULT_NAMESPACE_ID);
    String serviceName = WebUtils.required(request, CommonParams.SERVICE_NAME);
    NamingUtils.checkServiceNameFormat(serviceName);
    Instance instance = parseInstance(request);
    serviceManager.registerInstance(namespaceId, serviceName, instance);
    return "ok";
}</code>

The

ConsistencyService

implementation is selected by

mapConsistencyService(key)

:

<code>private ConsistencyService mapConsistencyService(String key) {
    return KeyBuilder.matchEphemeralKey(key) ? ephemeralConsistencyService : persistentConsistencyService;
}</code>

AP Mode

AP mode uses the Distro protocol via

EphemeralConsistencyService

. The

put

method persists data and notifies other nodes.

<code>public void put(String key, Record value) throws NacosException {
    onPut(key, value);
    distroProtocol.sync(new DistroKey(key, KeyBuilder.INSTANCE_LIST_KEY_PREFIX), DataOperation.CHANGE,
            globalConfig.getTaskDispatchPeriod() / 2);
}</code>

CP Mode

CP mode relies on Raft via

PersistentConsistencyServiceDelegateImpl

. Its

put

method forwards the update to the Raft core.

<code>public void put(String key, Record value) throws NacosException {
    checkIsStopWork();
    try {
        raftCore.signalPublish(key, value);
    } catch (Exception e) {
        Loggers.RAFT.error("Raft put failed.", e);
        throw new NacosException(NacosException.SERVER_ERROR, "Raft put failed, key:" + key + ", value:" + value, e);
    }
}</code>

During

signalPublish

, the leader node persists the datum, notifies peers, and waits for a majority of acknowledgments before confirming success.

<code>public void signalPublish(String key, Record value) throws Exception {
    if (!isLeader()) {
        // forward to leader
        return;
    }
    OPERATE_LOCK.lock();
    try {
        // build datum and publish
        CountDownLatch latch = new CountDownLatch(peers.majorityCount());
        // async post to peers
        // await majority
        if (!latch.await(UtilsAndCommons.RAFT_PUBLISH_TIMEOUT, TimeUnit.MILLISECONDS)) {
            throw new IllegalStateException("data publish failed, caused failed to notify majority, key=" + key);
        }
    } finally {
        OPERATE_LOCK.unlock();
    }
}</code>

Whether a client instance is stored in AP or CP mode depends on the

ephemeral

flag during registration:

ephemeral=true

uses AP (Distro), while

ephemeral=false

uses CP (Raft).

Nacos Source Debugging

Nacos Startup Files

Locate the Nacos startup JAR (

target/nacos-server.jar

) and extract it:

<code># Extract the JAR
tar -zxvf nacos-server.jar

# View MANIFEST.MF
cat META-INF/MANIFEST.MF</code>

The

Start-Class

entry reveals the Spring Boot entry point

com.alibaba.nacos.Nacos

, which can be run directly from an IDE for debugging.

References

http://nacos.io

https://github.com/alibaba/nacos/issues/3000

source code analysisNacosservice registrationspring-cloud-alibabaAP/CP
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

login 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.