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-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.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:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>The client initiates registration via Spring events. The core auto‑configuration class is NacosServiceRegistryAutoConfiguration, which defines three beans:
NacosServiceRegistry NacosRegistration NacosAutoServiceRegistrationNacosAutoServiceRegistration
This class extends AbstractAutoServiceRegistration and implements ApplicationContextAware and ApplicationListener<WebServerInitializedEvent>. After the container starts, onApplicationEvent triggers bind(event), which eventually calls start() and then register() to invoke serviceRegistry.register().
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());
}NacosServiceRegistry
The register method creates a NamingService instance, builds an Instance from the registration data, and calls namingService.registerInstance.
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);
}
}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.
@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";
}The ConsistencyService implementation is selected by mapConsistencyService(key):
private ConsistencyService mapConsistencyService(String key) {
return KeyBuilder.matchEphemeralKey(key) ? ephemeralConsistencyService : persistentConsistencyService;
}AP Mode
AP mode uses the Distro protocol via EphemeralConsistencyService. The put method persists data and notifies other nodes.
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);
}CP Mode
CP mode relies on Raft via PersistentConsistencyServiceDelegateImpl. Its put method forwards the update to the Raft core.
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);
}
}During signalPublish, the leader node persists the datum, notifies peers, and waits for a majority of acknowledgments before confirming success.
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();
}
}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:
# Extract the JAR
tar -zxvf nacos-server.jar
# View MANIFEST.MF
cat META-INF/MANIFEST.MFThe 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
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.
