Last time we introduced ZeroMQ and also talked about the fact that there was a native C# port by way of the NetMQ library, which as I said we will be using from here on out. I also mentioned that the power of ZeroMQ comes from a bunch of pre-canned sockets, which you can use as building blocks to build massive or small topologies.
From the ZeroMQ guide it states this:
The built-in core ØMQ patterns are:
Request-reply, which connects a set of clients to a set of services. This is a remote procedure call and task distribution pattern.
Pub-sub, which connects a set of publishers to a set of subscribers. This is a data distribution pattern.
Pipeline, which connects nodes in a fan-out/fan-in pattern that can have multiple steps and loops. This is a parallel task distribution and collection pattern.
Exclusive pair, which connects two sockets exclusively. This is a pattern for connecting two threads in a process, not to be confused with “normal” pairs of sockets.
There are of course certain well known patterns, that the chaps that wrote Zero have come across, and talk about in the book and the guide (links here again in case you missed them last time)
I personally think it is a very clever thing to have done, to give certain sections of topologies an actual pattern name, as it means you can Google around a certain pattern name. For example I may Google “Lazy Pirate Pattern C#”, and I would know that the results would almost certainly be talking about the exact socket arrangement I had in mind. So yeah good idea giving these things names.
Standard ZeroMQ Socket Types
Anyway enough chit chat, lets get to the crux of what I wanted to talk about this time, which is the different socket types within ZeroMQ.
Zero actual has the following socket types:
This is known as a PublisherSocket in NetMQ, and can be used to publish messages.
This is known as a SubscriberSocket in NetMQ, and can be used to subscribe to message(s) (you can fill in a subscription topic which indicates which published messages you care about)
This is known as a XPublisherSocket in NetMQ and can be used to publish messages. XPUB and XSUB are used where you may have to bridge different networks.
This is known as a XSubscriberSocket in NetMQ. and can be used to subscribe to message(s) (you can fill in a subscription topic which indicates which published messages you care about). XPUB and XSUB are used where you may have to bridge different networks.
This is known as a RequestSocket in NetMQ. Is a synchronous blocking socket, that would initiate a request message.
This is known as a ResponseSocket in NetMQ. Is a synchronous blocking socket, that would provide a response to a message.
This is known as a RouterSocket in NetMQ. Router is typically a broker socket (but not limited to), and provides routing where it would more than likely know how to route messages back to the calling socket, thus its name of “Router”. It is fully asynchronous (non blocking).
The ROUTER socket, unlike other sockets, tracks every connection it has, and tells the caller about these. The way it tells the caller is to stick the connection identity in front of each message received. An identity, sometimes called an address, is just a binary string with no meaning except “this is a unique handle to the connection”. Then, when you send a message via a ROUTER socket, you first send an identity frame.
This is known as a DealerSocket in NetMQ. Dealer is typically a worker socket, and doesn’t provide any routing (ie it doesn’t know about the calling sockets identity), but it is fully asynchronous (non blocking)
This is known as a PushSocket in NetMQ. This would typically be used to push messages at worker, within a pipeline pattern
This is known as a PullSocket in NetMQ. This would one part of a work within a pipeline pattern, which would pull from a PUSH socket and the n do some work.
This is known as a PairSocket in NetMQ.
Standard ZeroMQ Socket Pairs
There are pretty strict recommendations about the pairing of the sockets we just discussed. The standard pairs of sockets that you should stick to using are shown below.
Any other combination will produce undocumented and unreliable results, and future versions of ZeroMQ will probably return errors if you try them
PUB and SUB
A standard Pub/Sub arrangement
XPUB and XSUB
A standard Pub/Sub arrangement
REQ and RES
A standard synchronous request/response arrangement
The REQ client must initiate the message flow. A REP server cannot talk to a REQ client that hasn’t first sent it a request. Technically, it’s not even possible, and the API also returns an EFSM error if you try it.
REQ and ROUTER
A standard synchronous request with an asynchronous server responding, where the router will know how to do the routing back the correct request socket
In the same way that we can replace REQ with DEALER ….we can replace REP with ROUTER. This gives us an asynchronous server that can talk to multiple REQ clients at the same time. If we rewrote the “Hello World” server using ROUTER, we’d be able to process any number of “Hello” requests in parallel.
We can use ROUTER in two distinct ways:
- As a proxy that switches messages between frontend and backend sockets.
- As an application that reads the message and acts on it.
In the first case, the ROUTER simply reads all frames, including the artificial identity frame, and passes them on blindly. In the second case the ROUTER must know the format of the reply envelope it’s being sent. As the other peer is a REQ socket, the ROUTER gets the identity frame, an empty frame, and then the data frame.
We will see more on what this means in subsequent posts
DEALER and REP
An asynchronous request with a synchronous server responding. When we use a standard REQ (ie not a DEALER for the client) socket, it does one extra thing for us, which is to include an empty frame. So when we switch to using a Dealer for the client, we need to do that part ourselves, by using SendMore, which we will get into within the next post.
If we rewrote the “Hello World” client using DEALER, we’d be able to send off any number of “Hello” requests without waiting for replies.
When we use a DEALER to talk to a REP socket, we must accurately emulate the envelope that the REQ socket would have sent, or the REP socket will discard the message as invalid. So, to send a message, we:
- Send an empty message frame with the MORE flag set; then
- Send the message body.
And when we receive a message, we:
- Receive the first frame and if it’s not empty, discard the whole message;
- Receive the next frame and pass that to the application.
DEALER and ROUTER
An asynchronous request with an asynchronous server responding, where the router will know how to do the routing back the correct request socket
With DEALER and ROUTER to get the most powerful socket combination, which is DEALER talking to ROUTER. It gives us asynchronous clients talking to asynchronous servers, where both sides have full control over the message formats.
Because both DEALER and ROUTER can work with arbitrary message formats, if you hope to use these safely, you have to become a little bit of a protocol designer. At the very least you must decide whether you wish to emulate the REQ/REP reply envelope. It depends on whether you actually need to send replies or not.
DEALER and DEALER
An asynchronous request with an asynchronous server responding (this should be used if the DEALER is talking to one and only one peer).
With a DEALER/DEALER, your worker can suddenly go full asynchronous, sending any number of replies back. The cost is that you have to manage the reply envelopes yourself, and get them right, or nothing at all will work. We’ll see a worked example later. Let’s just say for now that DEALER to DEALER is one of the trickier patterns to get right, and happily it’s rare that we need it.
ROUTER and ROUTER
An asynchronous request with an asynchronous server responding.
This sounds perfect for N-to-N connections, but it’s the most difficult combination to use. You should avoid it until you are well advanced with ØMQ
PUSH and PULL
Push socket connected to a Pull, which you may see in a divide and conquer type arrangement.
PAIR and PAIR
- Pair sockets should ONLY talk to another pair, it is a well defined pair, Typically you would use this for connecting two threads in a process