Skip to content
Nacos Service Registration & Discovery: Source Code Deep Dive with Spring Cloud Integration

Nacos Service Registration & Discovery: Source Code Deep Dive with Spring Cloud Integration

This article analyzes Nacos service registration and discovery internals, covering architecture, Spring Cloud Commons integration, heartbeat-based health checks, OpenAPI registration flow, and the HostReactor's three-map caching mechanism with 10-second periodic updates and failover support.

Architect's Guide
Architect's Guide
Architect's Guide

1. Nacos Introduction

Nacos solves unified configuration, service registration, and discovery in microservices. It integrates registry and config center with features: service discovery via DNS/RPC, real-time health checks (Ping/TCP, HTTP, MySQL), dynamic configuration management, weighted DNS routing, and service/metadata lifecycle management.

2. Nacos Registry Implementation Principles

2.1 Architecture

Key modules:

Provider APP : service provider

Consumer APP : service consumer

Name Server : routes to Nacos HA cluster via Virtual IP or DNS

Nacos Server : provides OpenAPI entry, Config Service, Naming Service, and Consistency Protocol (Raft for cluster data sync)

Nacos Console : management UI

Providers access Nacos Server via VIP, register/query via OpenAPI; cluster nodes sync via Raft.

2.2 Registry Principles

Registration: instances register on startup, deregister on shutdown. Discovery: consumers query registry for available instances. Health checks: registry calls instance health APIs (transport/app layer).

3. Nacos Source Code Analysis

3.1 Service Registration

Spring Cloud defines ServiceRegistry<R extends Registration> interface with register, deregister, close, setStatus, getStatus. Nacos implements via NacosServiceRegistry in spring-cloud-starter-alibaba-nacos-discovery which depends on spring-cloud-commons.

Auto-configuration: spring.factories declares AutoServiceRegistrationAutoConfiguration (conditional on spring.cloud.service-registry.auto-registration.enabled, default true). It injects AutoServiceRegistration and AutoServiceRegistrationProperties. AbstractAutoServiceRegistration<R> implements ApplicationListener<WebServerInitializedEvent>. On WebServerInitializedEvent, onApplicationEvent calls bind(event) which eventually invokes NacosServiceRegistry.register(). NacosServiceRegistry.register() delegates to NamingService.registerInstance(serviceId, group, instance):

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 instanceInterval = instance.getInstanceHeartBeatInterval();    beatInfo.setPeriod(instanceInterval == 0L ? DEFAULT_HEART_BEAT_INTERVAL : instanceInterval);    // 1. Heartbeat for health monitoring    this.beatReactor.addBeatInfo(NamingUtils.getGroupedName(serviceName, groupName), beatInfo);  }  // 2. Actual registration    this.serverProxy.registerService(NamingUtils.getGroupedName(serviceName, groupName), groupName, instance);}
BeatReactor.addBeatInfo()

stores beatInfo in dom2Beat map and schedules BeatTask every period (default 5000ms) via executorService.schedule(). Each beat sends a packet; missing response indicates failure. serverProxy.registerService() builds a parameter Map (namespaceId, serviceName, groupName, clusterName, ip, port, weight, enable, healthy, ephemeral, metadata) and POSTs to NACOS_URL_INSTANCE via OpenAPI.

Debug Walkthrough: Registration Flow

Project init triggers AbstractAutoServiceRegistration.onApplicationEvent() on WebServerInitializedEvent. NacosAutoServiceRegistration calls super.register(). NacosServiceRegistry.register() executes with serviceId, group, instance. registerInstance() does health check via beatReactor.addBeatInfo() then registration via serverProxy.registerService().

Registration Summary (Q&A)

Q1: Why spring-cloud-commons ? It provides ServiceRegistry interface; all Spring Cloud registry implementations must implement it. Nacos provides NacosServiceRegistry.

Q2: Service not registering? Must be a web project ( spring-boot-starter-web) because registration triggers on WebServerInitializedEvent.

Q3: Other roles of spring-cloud-commons ? Its spring.factories enables auto-configuration of AutoServiceRegistrationAutoConfiguration which injects AutoServiceRegistration (implemented by NacosAutoServiceRegistration) and wires the event listener.

Overall flow: startup → spring.factories auto-config → AutoServiceRegistrationAutoConfiguration → injects AutoServiceRegistration → listener on WebServerInitializedEvent → NacosServiceRegistry.register() → NamingService.registerInstance() → heartbeat + OpenAPI registration.

3.2 Service Discovery

Discovery occurs during remote calls (e.g., OpenFeign). @FeignClient("test-application") uses service name for discovery. NacosServerList.getServers() calls

discoveryProperties.namingServiceInstance().selectInstances(serviceId, group, true)

and converts List<Instance> to List<NacosServer>. NacosNamingService.selectInstances() delegates to overloaded method with clusters, healthy, subscribe flags. If subscribe=true, uses local cache via hostReactor.getServiceInfo(); else fetches directly from server via getServiceInfoDirectlyFromServer(). HostReactor maintains three maps:

private final Map<String, ScheduledFuture<?>> futureMap;  // async update callbacksprivate Map<String, ServiceInfo> serviceInfoMap;       // local service list, key=service nameprivate Map<String, Object> updatingMap;           // pending updatesprivate ScheduledExecutorService executor;              // periodic updates
getServiceInfo(serviceName, clusters)

:

If failover switch on, return from failover reactor.

Get ServiceInfo via getServiceInfo0(). If null, create new ServiceInfo, put into serviceInfoMap and updatingMap, then call updateServiceNow() to fetch from server immediately, remove from updatingMap.

If updatingMap contains key, wait up to 5000ms on serviceObj for update to complete.

Schedule periodic updates via scheduleUpdateIfAbsent().

Return ServiceInfo from serviceInfoMap. scheduleUpdateIfAbsent() creates an UpdateTask scheduled every 10 seconds via executor, stores ScheduledFuture in futureMap. This ensures async background refresh regardless of call frequency.

Debug Walkthrough: Discovery Flow

Remote call (Feign) triggers NacosServerList.getServers() with serviceId matching @FeignClient value.

Calls NacosNamingService.selectInstances() with subscribe=true → reads from local serviceInfoMap. HostReactor.getServiceInfo() retrieves from serviceInfoMap; uses updatingMap and 5s wait for consistency.

Always calls scheduleUpdateIfAbsent() to ensure periodic refresh.

Background UpdateTask every 10s updates local registry; changes reflected in serviceInfoMap.

Discovery Summary

Discovery typically uses subscription model → reads from local cache ( serviceInfoMap).

Non-subscribed calls fetch from server directly (requires healthy server).

Three maps coordinate: serviceInfoMap (data), updatingMap (in-flight), futureMap (async callbacks). getServiceInfo() combines immediate fetch ( updateServiceNow) and scheduled refresh ( scheduleUpdateIfAbsent).

Async updates via thread pool store futures in futureMap and refresh local registry on completion.

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.

MicroservicesService DiscoverySource Code AnalysisNacosService RegistrationSpring CloudRaftHealth Check
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.