5
votes

I was implemented a simple request-reply architecture with a router using ZeroMQ. This works correctly for PyZMQ version 2.1.11. Unfortunately, when I test it on PyZMQ version 14.0.0, sender (REQ) can send to the router then router received its message and send to receiver (REP) but the receiver does not receive the message! I encounter to this problem when I upgraded PyZMQ from version 2.1.11 to 14.0.0.

REQ <-> ROUTER <-> REP

Here is my code:

sender.py

import zmq
import time

if __name__=='__main__':
    context = zmq.Context()
    socket = context.socket(zmq.REQ)
    socket.setsockopt(zmq.IDENTITY, "S")
    socket.connect("tcp://127.0.0.1:6660")
    i = 0
    while True:
        i += 1
        socket.send("R", zmq.SNDMORE)
        socket.send("", zmq.SNDMORE)
        socket.send("Message: %d" % i)
        print("Message : %d sent" % i)
        fromAddr = socket.recv()
        empty = socket.recv()
        resp = socket.recv()
        print("%s received!" % str(resp))
        time.sleep(1)

router.py

import zmq
import time

if __name__=='__main__':
    context = zmq.Context()
    frontend = context.socket(zmq.ROUTER)
    frontend.bind("tcp://*:6660")

    poll = zmq.Poller()
    poll.register(frontend, zmq.POLLIN)

    while True:
        sockets = dict(poll.poll(100))
        if frontend in sockets:
            if sockets[frontend] == zmq.POLLIN:
                fromAddr = frontend.recv()
                empty = frontend.recv()
                toAddr = frontend.recv()
                empty = frontend.recv()
                msg = frontend.recv()
                print("Message received from %s must be send to %s [%s]" % (str$
                frontend.send(toAddr, zmq.SNDMORE)
                frontend.send("", zmq.SNDMORE)
                frontend.send(fromAddr, zmq.SNDMORE)
                frontend.send("", zmq.SNDMORE)
                frontend.send(msg)
                print("Message has been send to %s!" % str(toAddr))

receiver.py

import zmq
import time

if __name__=='__main__':
    context = zmq.Context()
    socket = context.socket(zmq.REP)
    socket.setsockopt(zmq.IDENTITY, "R")
    socket.connect("tcp://127.0.0.1:6660")
    while True:
        print("Wating for request...")
        toAddr = socket.recv()
        empty = socket.recv()
        req = socket.recv()
        print("%s received!" % str(req))
        socket.send(toAddr, zmq.SNDMORE)
        socket.send(empty, zmq.SNDMORE)
        socket.send("Reply to %s" % str(req))

When I use this architecture:

routing with DEALER

The DEALER does not route to multiple receivers. DEALER only use round-robin method for sending messages to receivers. If ROUTER could be used instead of DEALER, then messages could be routed to specific receivers and will do round-robin between those.

2
it seems like a "hello world" example. Could you simplify it even more ? If it breaks without with/without setting an option; don't set the option (you may mention that adding it doesn't help). If it breaks with a single .send() call; use a single call, etc. Could you describe, why do you use such an architecture and shouldn't there be DEALER somewhere?jfs
This architecture with single sender and single receiver may be seem silly. In real world applications, you should have more senders and receivers than one. The router must route messages between them like a network router. So, every message should contain source and destination addresses in addition to the body. This architecture gives a fully asynchronous communication between some senders and some receivers. Every senders can send a message to every receiver by target address (IDENTITY). We use DEALER for something like load balancing (round-robin) not routing.GTavasoli

2 Answers

3
votes

A ROUTER to REP socket is an invalid combination as explained here: http://zguide.zeromq.org/page:all#Request-Reply-Combinations

3
votes

According to what @nos said, ROUTER to REP is invalid combination but the ROUTER to ROUTER socket is valid one. Simply, I changed the REP socket to ROUTER! Revised code is here:

import zmq

if __name__=='__main__':
    context = zmq.Context()
    socket = context.socket(zmq.ROUTER)       # Changed
    socket.setsockopt(zmq.IDENTITY, "R1")
    socket.connect("tcp://127.0.0.1:6660")
    while True:
        print("Wating for request...")

        me = socket.recv()       # New
        empty = socket.recv()    # New
        toAddr = socket.recv()
        empty = socket.recv()
        req = socket.recv()
        print("%s received!" % str(req))

        socket.send(me, zmq.SNDMORE)       # New
        socket.send(empty, zmq.SNDMORE)    # New
        socket.send(toAddr, zmq.SNDMORE)
        socket.send(empty, zmq.SNDMORE)
        socket.send("Reply to %s" % str(req))