Cloud Native 55 min read

Nacos Service Registration and Discovery: Principles and Implementation

The article explains Nacos’s open‑source service registry and discovery mechanisms, detailing client auto‑configuration, registration and health‑check workflows, server‑side instance handling, asynchronous copy‑on‑write processing, heartbeat cleanup, and cluster synchronization, while comparing its AP/CP capabilities to Zookeeper and Eureka.

DeWu Technology
DeWu Technology
DeWu Technology
Nacos Service Registration and Discovery: Principles and Implementation

This article introduces Nacos, an open‑source middleware from Alibaba for managing distributed micro‑services. It explains why a service registry is needed, outlines core features such as dynamic service registration, configuration, health checks, and the CAP theorem implications for distributed systems.

It compares three popular registries—Zookeeper (CP), Eureka (AP), and Nacos (supports both CP and AP)—and focuses on Nacos’s AP‑mode registration mechanism.

Client Registration Process

The Nacos client registers itself via Spring Boot auto‑configuration. The key classes are @Configuration annotated NacosServiceRegistryAutoConfiguration, which defines three beans: nacosServiceRegistry(), nacosRegistration(), and nacosAutoServiceRegistration(). The register() method of NacosAutoServiceRegistration is invoked on application start, sending a REST request to the Nacos server.

public class NacosServiceRegistryAutoConfiguration {
    @Bean
    public NacosServiceRegistry nacosServiceRegistry(NacosDiscoveryProperties props) {
        return new NacosServiceRegistry(props);
    }
    @Bean
    public NacosRegistration nacosRegistration(ObjectProvider<List<NacosRegistrationCustomizer>> cp,
                                               NacosDiscoveryProperties props, ApplicationContext ctx) {
        return new NacosRegistration(cp.getIfAvailable(), props, ctx);
    }
    @Bean
    public NacosAutoServiceRegistration nacosAutoServiceRegistration(NacosServiceRegistry reg,
                                                                   AutoServiceRegistrationProperties asp,
                                                                   NacosRegistration regBean) {
        return new NacosAutoServiceRegistration(reg, asp, regBean);
    }
}

During onApplicationEvent, the client builds an Instance object and calls namingService.registerInstance, which ultimately invokes the server‑side API /instance.

Client Service Discovery

The client first checks a local cache; if missing, it calls /instance/list to pull the full instance list. A scheduled task periodically refreshes the cache. The discovery code extracts metadata, filters by health, and builds a list of Instance objects.

public List<Instance> getAllInstances(String serviceName, String groupName,
                                 List<String> clusters, boolean subscribe) throws NacosException {
    ServiceInfo info;
    if (subscribe) {
        info = hostReactor.getServiceInfo(...);
    } else {
        info = hostReactor.getServiceInfoDirectlyFromServer(...);
    }
    return (info == null) ? new ArrayList<>() : info.getHosts();
}

Server‑Side Registration Logic

When the server receives a registration request, it creates a Service object (if absent) and stores the instance in memory. The core method is registerInstance in NamingService, which calls addInstance and then persists the data via the consistency service.

public void registerInstance(String namespaceId, String serviceName, Instance instance) throws NacosException {
    createEmptyService(namespaceId, serviceName, instance.isEphemeral());
    Service service = getService(namespaceId, serviceName);
    addInstance(namespaceId, serviceName, instance.isEphemeral(), instance);
}

The addInstance method builds a key, updates the in‑memory Service object, and writes an Instances record to the consistencyService. The consistency service then notifies listeners via the Notifier thread.

public void addInstance(String namespaceId, String serviceName, boolean ephemeral, Instance... ips) {
    String key = KeyBuilder.buildInstanceListKey(namespaceId, serviceName, ephemeral);
    Service service = getService(namespaceId, serviceName);
    synchronized (service) {
        List<Instance> list = addIpAddresses(service, ephemeral, ips);
        Instances instances = new Instances();
        instances.setInstanceList(list);
        consistencyService.put(key, instances);
    }
}

The Notifier runs a blocking loop, taking Pair<String, DataOperation> tasks from a queue and invoking listeners’ onChange methods, which finally update the service’s instance list.

while (true) {
    Pair<String, DataOperation> pair = tasks.take();
    handle(pair);
}

Heartbeat and Cleanup

Each Service schedules a ClientBeatCheckTask that runs every 5 seconds. It marks instances as unhealthy if the last heartbeat exceeds the timeout (default 15 s) and removes them after a longer grace period (default 30 s).

for (Instance instance : instances) {
    if (System.currentTimeMillis() - instance.getLastBeat() > instance.getInstanceHeartBeatTimeOut()) {
        instance.setHealthy(false);
    }
    if (System.currentTimeMillis() - instance.getLastBeat() > instance.getIpDeleteTimeout()) {
        deleteIp(instance);
    }
}

Asynchronous Registration & Data Sync

Nacos uses a lock‑free, copy‑on‑write approach. Registration requests are first placed into an in‑memory blocking queue ( Notifier), then processed by a dedicated thread, which updates a copy of the instance list and swaps it atomically. This design avoids read‑write contention and supports high concurrency.

For cluster mode, the DistroProtocol synchronizes changes to other nodes. After a put, distroProtocol.sync creates a DistroDelayTask for each remote member. A delayed task engine converts it to an DistroSyncChangeTask, which performs an HTTP POST to the target node’s /instance endpoint. Failed syncs are retried via a DistroFailedTaskHandler.

public void sync(DistroKey key, DataOperation action, long delay) {
    for (Member m : memberManager.allMembersWithoutSelf()) {
        DistroKey target = new DistroKey(key.getResourceKey(), key.getResourceType(), m.getAddress());
        DistroDelayTask task = new DistroDelayTask(target, action, delay);
        distroTaskEngineHolder.getDelayTaskExecuteEngine().addTask(target, task);
    }
}

During node startup, DistroLoadDataTask performs a full snapshot sync from an existing member, ensuring new nodes receive the complete service registry.

Summary

Nacos achieves high‑throughput service registration by decoupling request handling from state mutation (queue + worker thread), employing copy‑on‑write for instance lists, and providing both pull‑based and UDP‑based push discovery. Its flexible AP/CP mode selection, heartbeat management, and cluster data synchronization make it a robust backbone for cloud‑native micro‑service architectures.

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.

Distributed SystemsJavaMicroservicesservice discoveryNacosSpring Boot
DeWu Technology
Written by

DeWu Technology

A platform for sharing and discussing tech knowledge, guiding you toward the cloud of technology.

0 followers
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.