# r-nacos Architecture Explained: How ConfigActor and NamingActor Interact

> Explore the r-nacos architecture, detailing how ConfigActor and NamingActor, independent Actix actors, manage configuration via Raft and service discovery, respectively, within a dual-plane design.

- Repository: [Nacos Group/r-nacos](https://github.com/nacos-group/r-nacos)
- Tags: architecture
- Published: 2026-03-07

---

**The r-nacos architecture implements a dual-plane design where independent Actix actors—`ConfigActor` managing configuration via Raft consensus and `NamingActor` handling service discovery—share the same binary and coordinate during cluster snapshots while maintaining separate state machines.**

The r-nacos project is a lightweight, Rust-based reimplementation of the Nacos platform, combining service discovery and configuration management into a single binary. Understanding the r-nacos architecture requires examining how its two primary actors—`ConfigActor` and `NamingActor`—operate as isolated state machines while sharing critical infrastructure for consensus and persistence.

## High-Level Architecture Overview

The r-nacos architecture follows a single-binary, multi-node Raft cluster model. The system runs two parallel data planes within the same process, each backed by independent consensus mechanisms:

| Component | Data Store | Consensus Protocol | Primary Actor |
|-----------|------------|-------------------|---------------|
| **Config Center** | Configuration key-value pairs (data-id, group, tenant) | Raft (async-raft) with replicated log and snapshot | `ConfigActor` in [`src/config/core.rs`](https://github.com/nacos-group/r-nacos/blob/main/src/config/core.rs) |
| **Naming Center** | Service definitions and instance metadata (IP, port, health status) | Raft (separate log) with distro-style sync for gRPC/HTTP | `NamingActor` in [`src/naming/core.rs`](https://github.com/nacos-group/r-nacos/blob/main/src/naming/core.rs) |

Both actors are initialized by the bean-factory framework at startup, as defined in [`src/starter.rs`](https://github.com/nacos-group/r-nacos/blob/main/src/starter.rs). The architecture diagram in [`book/src/architecture.md`](https://github.com/nacos-group/r-nacos/blob/main/book/src/architecture.md) illustrates how these planes expose unified HTTP/gRPC interfaces while maintaining isolated persistence layers.

## ConfigActor: The Configuration State Machine

Located in [`src/config/core.rs`](https://github.com/nacos-group/r-nacos/blob/main/src/config/core.rs) at line 397, `ConfigActor` encapsulates the configuration management plane as an Actix actor with its own Raft-backed state machine.

### Core Responsibilities

`ConfigActor` maintains an in-memory cache of configuration data:

```rust
#[bean(inject)]
pub struct ConfigActor {
    pub(crate) cache: HashMap<ConfigKey, ConfigValue>,
    pub(crate) listener: ConfigListener,
    raft: Option<Weak<NacosRaft>>,
    // ...
}

```

The actor handles client subscription requests for push-based configuration updates and processes write operations through Raft commands (`ConfigRaftCmd`). When the leader receives a configuration change, it appends a log entry via `set_config` (around line 630), replicates it to followers, and updates the local `cache` upon commit.

### Raft Integration and Snapshots

`ConfigActor` implements snapshot logic for Raft persistence through `build_snapshot` and `load_snapshot_record`. It also provides a `transfer_backup` method for exporting the entire configuration database.

## NamingActor: The Service Discovery State Machine

Defined in [`src/naming/core.rs`](https://github.com/nacos-group/r-nacos/blob/main/src/naming/core.rs) at line 82, `NamingActor` manages the service discovery plane with distinct consensus mechanisms for different data types.

### Core Responsibilities

`NamingActor` maintains a map of services to their instances:

```rust
#[bean(inject)]
pub struct NamingActor {
    pub(crate) service_map: HashMap<ServiceKey, Service>,
    // ...
    pub(crate) raft_router: Option<Arc<RaftRequestRoute>>,
}

```

The actor handles instance registration, heartbeat management, and client queries via `NamingCmd` messages. It runs periodic health checks through `instance_time_out_heartbeat` and performs empty-service cleanup.

### Raft Routing and Distro Sync

Unlike `ConfigActor`, which uses direct Raft integration, `NamingActor` routes persistent instance changes through `raft_router` via `process_naming_raft_request`. For high-throughput ephemeral registrations, it employs a *distro*-style synchronization mechanism through `build_distro_instances` to replicate data across the cluster using gRPC/HTTP protocols.

## How ConfigActor and NamingActor Interact

While `ConfigActor` and `NamingActor` operate as independent state machines, they cooperate at critical junctures in the r-nacos architecture.

### Bootstrap and Dependency Injection

Both actors are instantiated together during application startup in [`src/starter.rs`](https://github.com/nacos-group/r-nacos/blob/main/src/starter.rs) (lines 65-73):

```rust
let (index_manager, config_addr) = create_actor_at_thread2(index_manager, ConfigActor::new());
let naming_addr = NamingActor::create_at_new_system();
// ...

```

The bean-factory framework injects shared resources, such as the Raft router and consensus infrastructure, into both actors during this phase. This ensures that while the actors maintain separate concerns, they share the underlying consensus infrastructure.

### Coordinated Snapshot Operations

The most significant interaction occurs during cluster snapshots. Both actors contribute their state to a shared `SnapshotWriterActor` defined in [`src/raft/filestore/raftsnapshot.rs`](https://github.com/nacos-group/r-nacos/blob/main/src/raft/filestore/raftsnapshot.rs).

When a snapshot is triggered, `ConfigActor::build_snapshot` (lines 555-564) writes configuration records:

```rust
// Inside ConfigActor::build_snapshot
writer.do_send(SnapshotWriterRequest::Record(config_record));

```

Simultaneously, `NamingActor::build_snapshot` (lines 1216-1244) writes permanent instance data:

```rust
// Inside NamingActor::build_snapshot
writer.do_send(SnapshotWriterRequest::Record(naming_record));

```

Because the same `SnapshotWriterActor` serializes both data streams, the resulting snapshot file contains a consistent view of both configuration and naming state. During node recovery or new node bootstrap, `load_snapshot_record` methods in both actors restore their respective states from this unified snapshot, ensuring the cluster resumes with synchronized configuration and service discovery data.

## Summary

- **r-nacos architecture** implements dual independent state machines—`ConfigActor` for configuration and `NamingActor` for service discovery—within a single Rust binary.
- **Both actors use Raft consensus**: `ConfigActor` embeds direct Raft integration via `async-raft`, while `NamingActor` routes persistent changes through a `raft_router` and uses distro sync for ephemeral instances.
- **Actors interact during bootstrap**: [`src/starter.rs`](https://github.com/nacos-group/r-nacos/blob/main/src/starter.rs) initializes both actors through the bean-factory framework, injecting shared consensus infrastructure.
- **Snapshot coordination ensures consistency**: Both actors write to the same `SnapshotWriterActor` during `build_snapshot` operations, producing unified cluster state files that restore both planes simultaneously.
- **Code locations**: `ConfigActor` resides in [`src/config/core.rs`](https://github.com/nacos-group/r-nacos/blob/main/src/config/core.rs) (line 397), `NamingActor` in [`src/naming/core.rs`](https://github.com/nacos-group/r-nacos/blob/main/src/naming/core.rs) (line 82), with snapshot coordination in [`src/raft/filestore/raftsnapshot.rs`](https://github.com/nacos-group/r-nacos/blob/main/src/raft/filestore/raftsnapshot.rs).

## Frequently Asked Questions

### What is the difference between ConfigActor and NamingActor in r-nacos?

`ConfigActor` manages configuration key-value pairs using direct Raft consensus, maintaining an in-memory `HashMap<ConfigKey, ConfigValue>` cache and handling push-based client notifications. `NamingActor` manages service discovery instances and service definitions, using a separate Raft router for persistent data and a distro-style synchronization mechanism for high-throughput ephemeral instance registrations.

### How do ConfigActor and NamingActor share the same Raft cluster?

Both actors participate in the same Raft cluster infrastructure but use separate state machines. `ConfigActor` embeds a `Weak<NacosRaft>` reference directly for consensus operations, while `NamingActor` uses an `Arc<RaftRequestRoute>` to route persistent changes. The [`src/starter.rs`](https://github.com/nacos-group/r-nacos/blob/main/src/starter.rs) bootstrap process injects these shared consensus components into both actors during initialization, ensuring they communicate with the same underlying Raft nodes.

### Why does r-nacos use a unified snapshot writer for both actors?

r-nacos uses a shared `SnapshotWriterActor` to ensure crash consistency across both configuration and naming data planes. When either `ConfigActor::build_snapshot` or `NamingActor::build_snapshot` is triggered, both serialize their state to the same writer, producing a single snapshot file. This guarantees that during node recovery or cluster expansion, both planes restore from the same point-in-time, preventing configuration and service discovery state desynchronization.

### Can ConfigActor and NamingActor run independently or be separated into different processes?

Currently, r-nacos architecture packages both actors into a single binary designed for co-location. While they maintain independent state machines and could theoretically be separated, the current implementation in [`src/starter.rs`](https://github.com/nacos-group/r-nacos/blob/main/src/starter.rs) initializes them together and relies on shared memory references for the snapshot writer and Raft infrastructure. Running them as separate processes would require significant refactoring of the consensus and persistence layers to use network protocols instead of Actix actor references.