Search code examples
python-3.xzeromqpyzmq

How do ZeroMQ HEARTBEAT sockopts() settings work?


I'm using python's pyzmq==22.2.1 which should support ZeroMQ 4.2.0 (according to the API)

I'm trying to make use of the heartbeat socket options (ZMQ_HEARTBEAT_IVL, ZMQ_HEARTBEAT_TIMEOUT and ZMQ_HEARTBEAT_TTL). However, when I set these socket options, I am not receiving the expected TimeoutException or any exception on my socket. It just seems to sit there doing nothing.

What is the expected behaviour after setting these socket options ? On the server side, how does the server detect the client has timeout and missed a heartbeat and vice versa for the client (is there an exception or something that's supposed to be thrown or something ?).

I've setup a simple router-dealer echo example below:

# Server Code:
import zmq
c = zmq.Context()
s = c.socket(zmq.ROUTER)
s.setsockopt(zmq.HEARTBEAT_IVL, 1000)
s.setsockopt(zmq.HEARTBEAT_TIMEOUT, 5000)
s.setsockopt(zmq.HEARTBEAT_TTL, 5000)
s.bind('tcp://127.0.0.1:5555')


while True:
    id, data = s.recv_multipart()
    s.send_multipart([id, data], zmq.NOBLOCK)



# Client Code
import zmq
import time
c = zmq.Context()
s = c.socket(zmq.DEALER)
s.HEARTBEAT_IVL = 1000
s.HEARTBEAT_TIMEOUT = 5000
s.connect('tcp://127.0.0.1:5555')
i = 0
while True:
    s.send(str(i).encode())
    print(s.recv())
    i += 1
    time.sleep(1)

Solution

  • Q : What is the expected behaviour after setting these socket options ?

    A :
    well,
    there are two-fold effect of the said settings. One, that actually works for your setup goals ( i.e. going & sending (most probably ZMTP/3.1) ZMTP_PING connection-oriented service-sublayer "ZMTP/3.1-service-packets" and reciprocally, not sure, but most often, adequately formed "ZMTP/{3.1|2.x|1.0}-service-packets" (hopefully delivered) back. These "service-packets" are visible on the wire-line (if present - an inproc://-transport-class and vmci://-transport-class too have no actual wire a typical user can hook-on and sniff-traffic in, but some kind of pointer-acrobatics used for RAM-mapping), so a protocol-analyser will "see" them id decodes like this:

    a local-initiator
            MAY send:
    
    +------+----+
    | %xNN | 24 |
    +------+----+
       0     1
     flags  size
    
    +------+---+---+---+---+
    | %xNN | P | I | N | G |
    +------+---+---+---+---+
       2     3   4   5   6  
              ZMTP/3.1-Command name "PING"
    
    +---+---+
    |       |
    +---+---+
      7    8  ping-ttl 2B
    
              MAY be zero  
              MAY be ttl stored as [0:15], being a 16-bit TTL in 1/100 [s] ~ max 6553 [s]
                     ttl provides a strong hint
                                 to the other peer to disconnect
                                 if no further traffic is received after that time. 
    
    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
    | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | 0 |
    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
      9   10  11  12  13  14  15  16  17  18  19  20  21  22  23  24
    
              ping-context (max 16B)
              
              MAY be zero
              MAY be context-specific context-payload, not more than 16B
    

    a remote-peer
             SHALL respond:
    
    +------+----+
    | %xNN | 22 |
    +------+----+
       0     1
     flags  size
    
    +------+---+---+---+---+
    | %xNN | P | O | N | G |
    +------+---+---+---+---+
       2     3   4   5   6  
              ZMTP/3.1-Command name "PONG"
    
    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
    | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | 0 |
    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
      7   8   9   10  11  12  13  14  15  16  17  18  19  20  21  22
    
              ping-context (echoed as obtained from PING)
    

    ZMTP/3.1 v/s ZMTP/3.0 v/s ZMTP/2.x v/s ZMTP/1.0 differences and details get solved during version-negotiation phase, once the sublayer of connection-setup services performs all low-level needed handshaking (re)negotiations among peers trying to agree on version-, auth- & security- related principles


    The second effect of these ( under-the-hood of the Context()-engine instance performed negotiations ) is that you shall never see any direct interaction with ZeroMQ-(abstract)-Message-Transport-Protocol ( ZMTP ) defined service-setup-&-maintenance-processes.

    We simply enjoy the (above ZMTP generated) exposed API-calls to setup, configure and harness "our" user-level operated Signalling / Messaging infrastructure meta-plane, that is based on all the know-how "hidden" under-the-hood (and should remain so - sure, unless one decides to roll up sleeves and help develop the ZeroMQ system towards its next generation)

    Q : ( is there an exception or something that's supposed to be thrown or something ? )

    A :
    This is why all above had to have been told first, as the due reasoning, for which there ought be a fair & honest answer no to your second question.