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.
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-alibabaas 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.RELEASENacos 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><dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency></code>The client initiates registration via Spring events. The core auto‑configuration class is
NacosServiceRegistryAutoConfiguration, which defines three beans:
NacosServiceRegistry NacosRegistration NacosAutoServiceRegistrationNacosAutoServiceRegistration
This class extends
AbstractAutoServiceRegistrationand implements
ApplicationContextAwareand
ApplicationListener<WebServerInitializedEvent>. After the container starts,
onApplicationEventtriggers
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
registermethod creates a
NamingServiceinstance, builds an
Instancefrom 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
ConsistencyServicedecides 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
ConsistencyServiceimplementation 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
putmethod 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
putmethod 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
ephemeralflag during registration:
ephemeral=trueuses AP (Distro), while
ephemeral=falseuses 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-Classentry 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
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.