Series · Designing a Netconf Manager · Part 3 of 4

The service

Growing the design one use case at a time: from one client and one node to many clients and many nodes.

2014 · 4 min read

With the protocol handled by the library, the service has a different job: take requests from clients to nodes and responses back, efficiently. So its design is driven less by components and more by goals:

  • save time and memory;
  • make sure every client gets its responses, in the order it asked;
  • use and reuse NETCONF sessions and node connections well;
  • stay robust when things go wrong.

The method is to start with the simplest flow and grow it to the real one.

1 client1 request · 1 nodeopen, send, close1 clientn requests · 1 nodequeue + reuse1 clientn requests · n nodesdispatcher + pooln clientsn requests · n nodesregistration + mapping
Designing by growing the use case: each step adds one need, and the design answers it.

One client, one request, one node

The steps are plain: receive the request; read it and gather what it needs, such as node details; open a NETCONF connection (socket, secure protocol session and NETCONF session together); form and send the RPC; receive the response and convert it to the client's format; send it back; close the connection.

Receiving needs a provider: RMI, JMS, a message queue, or something of your own if you are feeling rebellious. Requests and responses deserve abstract structures, so clients can bring their own models. A task object maps the socket, secure protocol and session to one another.

On performance, the cost hides in reading the request. Receiving it as an object rather than as XML saves parsing time and memory, and the request should carry enough data for the services to find what they need quickly. The socket also needs a dedicated listener, which means a node-listening thread.

One client, many requests, one node

Following the same steps, every request would open and close its own connection. That wastes time, so connections must be reused, and closed only after they have been idle for a while.

Order matters too. Responses can be matched to requests by an identifier, but requests must reach the node in the order the client sent them; reordering reads and writes can do real damage. That calls for a queue on the way in and on the way out. (A broker like ActiveMQ can provide those queues.)

One client, many requests, many nodes

With many nodes, requests can run in parallel, one at a time per node, in order per node. That is a dispatcher with a thread pool, controlling how many operations run at once. One thread per node connection sounds tidy in theory; in practice, batches always treat the service and the processor better. Another option is several inbound queues, each owning a set of nodes, with a dispatcher per queue.

Many clients, many requests, many nodes

Now the service must know which request came from which client and return each response to the right one. Each client gets its own queue, through a simple registration, but node connections are not tied to clients, because that would multiply connections for no reason. NETCONF helps here: a response carries the same message-id as its request, so the mapping comes almost free.

The components that fall out

Client Aown queueClient Bown queueRequestdispatcherthread poolorder per nodetraffic controlTask mapTask · node 1Task · node 2Task · node 3NodesListenerNIO selectorResponsedispatchersupervisor closes idle tasks
The service at full size. Each client registers and gets its own queue; one task per node is reused across clients; message-ids map every response back to its request.

Nine objects appear across those steps, and they settle into three components.

  1. Client and service communication: request and response objects (abstract, with basic implementations), a queue system per client, and a provider service. A request needs a unique id, a client id, a node name, an operation and data; a response needs the ids, the node and the data.
  2. Request and response dispatching: the core of efficiency. The dispatchers work concurrently over the queues, keep node and client networks from flooding, and reuse connections through a concurrent map of tasks. A supervisor closes idle ones.
  3. Service and node communication: the task (session, secure protocol session and socket) and a dedicated listener for each, best built like a Java NIO selector.

Next: subscriptions and notifications.

Drafted in 2014
Updated for site in 2026