The REQ/REP socket pair we explored makes request/reply logic easy to code by operating sequentially. The Node.js code for a given responder will only ever be aware of one message at a time.
For parallel message processing, ØMQ includes the more advanced socket types ROUTER and DEALER. Let’s explore these a bit; then we’ll be ready to construct our Node.js cluster.
You can think of a ROUTER socket as a parallel REP socket. Rather than replying to only one message at a time, a ROUTER socket can handle many requests simultaneously. It remembers which connection each request came from and will route reply messages accordingly.
Recall from Implementing a Messaging Protocol, that any time you do networked programming, you’re working with one or more protocols. ØMQ uses the ZeroMQ Message Transport Protocol (ZMTP) for exchanging messages.[30] This protocol uses a sequence of low-overhead frames to compose messages. A ROUTER socket uses these frames to route each reply message back to the connection that issued the request.
Most of the time your Node.js programs can ignore the underlying details of ØMQ frames because the simpler socket types only need one frame per message. But the ROUTER socket type uses multiple frames.
Here’s an example of how to create a ROUTER socket in Node.js, with a message handler that grabs all the incoming frames:
| | const router = zmq.socket('router'); |
| | router.on('message', (...frames) => { |
| | // Use frames. |
| | }); |
Previously our message handlers would take a data parameter, but this handler takes an array of frames instead.
The three dots (...) introduce an ECMAScript 2015 feature called rest parameters. When used in a function declaration, this syntax allows you to collect any number of arguments passed into the function as an array.
Now that we can get all the frames, let’s look at the DEALER socket type.
If a ROUTER socket is a parallel REP socket, then a DEALER is a parallel REQ. A DEALER socket can send multiple requests in parallel.
Let’s see how a dealer and router work together in Node. Take a look at this code sample:
| | const router = zmq.socket('router'); |
| | const dealer = zmq.socket('dealer'); |
| | |
| | router.on('message', (...frames) => dealer.send(frames)); |
| | dealer.on('message', (...frames) => router.send(frames)); |
Here we create both a ROUTER socket and a DEALER socket. Whenever either receives a message, it sends the frames to the other socket.
This creates a passthrough relationship where incoming requests to the router will be passed off to the dealer to send out to its connections. Likewise, incoming replies to the dealer will be forwarded back to the router, which directs each reply back to the connection that requested it.
The following figure shows the structure we’ll implement shortly, using the techniques we’ve just discussed.

The box in the center of the figure is the Node.js program. An incoming REQ socket connects to the ROUTER. When the REQ socket issues a request, the ROUTER bounces it over to the DEALER. The DEALER then picks the next one of the REP sockets connected to it (round-robin style) and forwards the request.
When the REP connection produces a reply, it follows the reverse route. The DEALER receives the reply and bounces it back to the ROUTER. The ROUTER looks at the message’s frames to determine its origin and sends the reply back to the connected REQ that sent the initial request.
From the perspective of the REQ and REP sockets, nothing has changed. Each still works on one message at a time. Meanwhile, the ROUTER/DEALER pair can distribute (round-robin) among the REQ and REP sockets connected on both ends.
Now we’re ready to develop a clustered Node.js application on top of the REQ, REP, ROUTER, and DEALER sockets we’ve just explored.