Search code examples
socketszeromqdistributed-computingdistributed-systemlow-latency

Can a ZeroMQ ROUTER socket make a spontaneous asynchronous request to a specific DEALER socket?


I'm reading the ZeroMQ Guide and came across the following passage in regards to the ROUTER socket and identities:

An application that uses a ROUTER socket to talk to specific peers can convert a logical address to an identity if it has built the necessary hash table. Because ROUTER sockets only announce the identity of a connection (to a specific peer) when that peer sends a message, you can only really reply to a message, not spontaneously talk to a peer.

This is true even if you flip the rules and make the ROUTER connect to the peer rather than wait for the peer to connect to the ROUTER. However you can force the ROUTER socket to use a logical address in place of its identity. The zmq_setsockopt reference page calls this setting the socket identity.

According to this passage, "you can only really reply to a message, not spontaneously talk to a peer", meaning a ROUTER can't send a message to a specific DEALER, but the next sentence implied that you can if you force the router socket to use a logical address: "However you can force the ROUTER socket to use a logical address in place of its identity". This part confuses me because they just said that you cant spontaneously send messages from a router to a dealer, but now they claim you can. If you follow this link to the guide, you'll see that after this passage, they say "It works as follows", but the steps they give don't seem to clear up how to send a spontaneous message from a ROUTER to a specific DEALER and return a response back to the original ROUTER.

My question: Is it possible for a single ROUTER socket to send a request to a specific DEALER (Out of many) socket and the DEALER send the result of the request back to the ROUTER? And if it is possible, how can this be done?

Follow up question: If this is not possible, is there a better combination of sockets to approach this?

Below is a crude diagram of my intended design:

enter image description here

Basically, the client send a request to 1 specific server, that server processes the request and returns the result of the request to the client. The client now has that result, and it knows what server it was processed on.


Solution

  • Is it possible for a single ROUTER socket to send a request to a specific DEALER (Out of many) socket and the DEALER send the result of the request back to the ROUTER? And if it is possible, how can this be done?

    No, this is to the best of my knowledge not possible - it tries a DEALER/ROUTER anti-pattern.

    is there a better combination of sockets to approach this?

    Yes, this could be possible in a bit wild approach to manually-driven identity-frames in ROUTER/ROUTER.

    While one can tweak the ROUTER-side with identity tricks, your problem fails at the DEALER-side, where a fair-queue and round-robin policies destroy your wished-to-have 1:1-relation and the pairing of the JobREQ:ResultREP will fail.


    A bit more global view into how these toys work inside :

    ZeroMQ archetypes are smart and multi-layered inside, beyond of what you typically work with when calling any of the API methods. Some insight into (simplified, maybe oversimplified) inner work might help in this :

                                                                              +------------------------------+  
                                                                              | Formal Behavioural Archetype |
                                                                              |                              |
                                                                              |                       ROUTER |
                                                                              +------------------------------+
                                                                              | BEHAVIOUR Management         |
              +------------------------------+                                |                              |
              | Formal Behavioural Archetype |                                |     HWM'd messages Dropped(*)| *unless ZMQ_ROUTER_MANDATORY set
              |                              |                                |                              |
              |                       DEALER |                                |                              |
              +------------------------------+                       <===.recv()   served in FAIR-QUEUED mode|
              | BEHAVIOUR Management         |                       .send()===>   served by identityFRAME id|
              | on                           |                                +------------------------------+ 
              | no peers                     |                                | IDENTITY Based Routing Policy| +may preset  ZMQ_CONNECT_ROUTING_ID for one next .connect() transport-setup call (ref. API for details)
              | or                           |                                |  <--prepend peer-Id on.recv()| +see setting ZMQ_ROUTING_ID and ZMQ_ROUTING_HANDOVER (ref. API for details)
              | all HWM'd in MUTE-STATE !Drop|                                |  -->remove  peer-Id on.send()|
     .send()===>   served in ROUND-ROBIN mode|                                |    +Drop if peer not visible | *unless ZMQ_ROUTER_MANDATORY set
     <===.recv()   served in FAIR-QUEUED mode|                                +------------------------------+
              +------------------------------+                                | QUEUE Management Department  |
              | QUEUE Management Department :|                                |                              |
              |                            ::|                                |                          |hwm|
              |                           :::|                                |   |                  |   |...|
              |hwm|              |   |   |hwm|                                |   |                  |   |.. |
              |...|              |hwm|   |...|                                |   |                  |hwm|.  |
              |.. |              |...|hwm|.. |                                |hwm|                  |...|   |
              |.  |              |.. |...|.  |                                +-:-|------------------|-:-|-:-|
              +-:-|--------------|-:-|-:-|:::+                                | TRANSPORT Handling Department|
              | TRANSPORT Handling Department|                                |---+                  +-------|
              |-------+          +-----------|                                |tcp|                  | ipc   |
              | inproc|          | ipc       |                                |-:-|                  |-:---:-|
              |-:-----|          |-:---:--:::|                                |.C |                  |.C |.C |
              |.C |   |          |.C |.C |.B |                                +-o-|------------------+-o---o-+
              +-o-|---+----------+-o---o---O-+                                  |                      |   |
                |                  |   |   ^                                    |                      |   |
                |                  |   |   |                                    |                      |   +----------------------.connect()->------- ... may be a { ROUTER | DEALER | REQ }-socket
                |                  |   |   |                                    +----------------------|--------------------------.connect()->------- ..
                |                  |   |   +-<--.connect()---[ROUTER].02----<--------------------------+
                |                  |   |   +-<--.connect()---[ROUTER].07--- .....
                |                  |   |   +-<--.connect()------[REP].41--- ....
                |                  |   +--------.connect()->----[REP].18--- ...
                |                  +------------.connect()->-[ROUTER].51--- ..
                +-------------------------------.connect()->----[REP].17--- .  (+ REP.send() message <=== sent must consist of an empty message part, the delimiter, followed by one or more body parts. )