Deep Dive into Nacos Service Registration and Discovery Mechanisms
This article explains the underlying principles of Nacos as a service registry, covering registration flow, health‑checking, dynamic service awareness, the architecture diagram, key source‑code components, and how service discovery works in Spring Cloud micro‑service environments.
1. Nacos Overview
Before discussing Nacos, we introduce the problems of service registration and discovery in micro‑service architectures. Consumers need to know the addresses of provider instances, and providers must keep their address lists up to date, which is solved by a service registry offering address management, registration, and dynamic awareness.
Key Features of Nacos
Service discovery and health monitoring (supports DNS and RPC)
Dynamic configuration management
Dynamic DNS routing with weight‑based load balancing
Service and metadata management
2. Nacos Registration Center Implementation
2.1 Architecture Diagram
The Nacos ecosystem consists of the following modules:
Provider APP : Service provider.
Consumer APP : Service consumer.
Name Server : Provides high‑availability routing via Virtual IP or DNS.
Nacos Server : Core Nacos service. OpenAPI – entry point for functionality. Config Service & Naming Service – configuration and naming modules. Consistency Protocol – Raft‑based data synchronization.
Nacos Console : Management UI.
Summary : Providers register through the VIP to the Nacos cluster, using OpenAPI; Nacos Server synchronizes data across nodes via Raft.
2.2 Registration Principle
Registration involves three steps: the instance registers on startup, health checking (heartbeat) is performed, and finally the instance is recorded in the registry.
public interface ServiceRegistry
{
void register(R registration);
void deregister(R registration);
void close();
void setStatus(R registration, String status);
T getStatus(R registration);
}In Nacos, NacosServiceRegistry implements this interface. The Spring Cloud auto‑configuration loads AutoServiceRegistrationAutoConfiguration , which injects AutoServiceRegistration . The concrete class NacosAutoServiceRegistration listens for WebServerInitializedEvent and triggers registration.
public void register(Registration registration) {
if (StringUtils.isEmpty(registration.getServiceId())) {
log.warn("No service to register for nacos client...");
} else {
String serviceId = registration.getServiceId();
String group = this.nacosDiscoveryProperties.getGroup();
Instance instance = getNacosInstanceFromRegistration(registration);
try {
this.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, e);
ReflectionUtils.rethrowRuntimeException(e);
}
}
}The registerInstance method creates a BeatInfo for heartbeat monitoring and then calls serverProxy.registerService to send an OpenAPI request.
public void registerInstance(String serviceName, String groupName, Instance instance) throws NacosException {
if (instance.isEphemeral()) {
BeatInfo beatInfo = new BeatInfo();
beatInfo.setServiceName(NamingUtils.getGroupedName(serviceName, groupName));
beatInfo.setIp(instance.getIp());
beatInfo.setPort(instance.getPort());
beatInfo.setCluster(instance.getClusterName());
beatInfo.setWeight(instance.getWeight());
beatInfo.setMetadata(instance.getMetadata());
beatInfo.setScheduled(false);
long interval = instance.getInstanceHeartBeatInterval();
beatInfo.setPeriod(interval == 0L ? DEFAULT_HEART_BEAT_INTERVAL : interval);
this.beatReactor.addBeatInfo(NamingUtils.getGroupedName(serviceName, groupName), beatInfo);
}
this.serverProxy.registerService(NamingUtils.getGroupedName(serviceName, groupName), groupName, instance);
}Heartbeat registration ( addBeatInfo ) schedules a periodic task that sends a ping to the server; if the server does not respond, the instance is considered unhealthy.
public void addBeatInfo(String serviceName, BeatInfo beatInfo) {
LogUtils.NAMING_LOGGER.info("[BEAT] adding beat: {} to beat map.", beatInfo);
String key = buildKey(serviceName, beatInfo.getIp(), beatInfo.getPort());
BeatInfo existBeat = dom2Beat.remove(key);
if (existBeat != null) {
existBeat.setStopped(true);
}
dom2Beat.put(key, beatInfo);
executorService.schedule(new BeatReactor.BeatTask(beatInfo), beatInfo.getPeriod(), TimeUnit.MILLISECONDS);
MetricsMonitor.getDom2BeatSizeMonitor().set((double) dom2Beat.size());
}3. Nacos Source Code Analysis
The analysis focuses on two aspects: service registration and service discovery.
3.1 Service Registration
The registration flow starts with AbstractAutoServiceRegistration listening to WebServerInitializedEvent . When the event fires, NacosAutoServiceRegistration calls super.register() , which ultimately invokes NacosServiceRegistry.register() . This method delegates to the Nacos client SDK's NamingService.registerInstance() .
3.2 Service Discovery
Discovery is triggered when a consumer (e.g., a Feign client) calls a remote interface. The Feign client uses NacosServerList.getServers() , which calls NacosNamingService.selectInstances() to obtain a list of healthy Instance objects.
public List
selectInstances(String serviceName, String groupName, boolean healthy) throws NacosException {
return this.selectInstances(serviceName, groupName, healthy, true);
}The HostReactor maintains three maps to cache service information:
serviceInfoMap – local cache of service metadata.
updatingMap – tracks services currently being refreshed.
futureMap – stores scheduled update tasks.
When a service is first requested, getServiceInfo() creates a ServiceInfo object, stores it in the cache, and triggers an immediate update via updateServiceNow() . It also schedules periodic updates with scheduleUpdateIfAbsent() , which creates a UpdateTask that runs every 10 seconds.
public void scheduleUpdateIfAbsent(String serviceName, String clusters) {
if (futureMap.get(ServiceInfo.getKey(serviceName, clusters)) == null) {
synchronized (futureMap) {
if (futureMap.get(ServiceInfo.getKey(serviceName, clusters)) == null) {
ScheduledFuture
future = addTask(new HostReactor.UpdateTask(serviceName, clusters));
futureMap.put(ServiceInfo.getKey(serviceName, clusters), future);
}
}
}
}Thus, discovery either reads from the local cache (when subscribe=true ) or queries the Nacos server directly (when subscribe=false ).
4. Summary
Nacos integrates registration and discovery into Spring Cloud via the ServiceRegistry interface. Registration occurs automatically after the web server starts, performing health checks and sending an OpenAPI registration request. Discovery retrieves instances through NacosNamingService , caching results locally and refreshing them periodically to ensure high availability.
Selected Java Interview Questions
A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!
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.