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.
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
Nine objects appear across those steps, and they settle into three components.
- 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.
- 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.
- 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