ZeroMQ - how to make a CLIENT to give up and try at a later time if the SERVER doesn't respond?
Asked Answered
W

1

3

Lets say I have a very simple Client/Server model, using REQ/REP from ZeroMQ. See python code below.

In the code below the client will wait forever, but I want the client to give up (lets say after 20 seconds) and move on with its life if it doesn't get a response. The server could be down, the router unplugged, the WiFi is not working. I really don't or should care why.

Then at a later time, I'll have the client try again and it could be a completely different request.

But I fear I'll cross an old request, get things out of order, cause more problems.

Does anyone know how to do this gracefully? I've been racking my brain on a simple solution.

SIMPLE CLIENT CODE

#!/usr/bin/env python3

import zmq
from time import sleep

#                                          CREATE SOCKET - Client (USING zmq.REQ)
my_client_context = zmq.Context()
my_client_socket = my_client_context.socket(zmq.REQ)
my_client_socket.connect('tcp://127.0.0.1:5557')

#                                          [REQ]uest AND [REP]ly
to_server = b"Hi"
my_client_socket.send(to_server)
from_server = my_client_socket.recv()
print(from_server)

sleep(2)

#                                          REQuest AND REPort
to_server = b"blah"
my_client_socket.send(to_server)
from_server = my_client_socket.recv()
print(from_server)

SIMPLE SERVER CODE

#!/usr/bin/env python3

import zmq

#                                         CREATE SOCKET - Server (USING zmq.REP)
my_server_context = zmq.Context()
my_server_socket = my_server_context.socket(zmq.REP)
my_server_socket.bind('tcp://127.0.0.1:5557')

#                                         LISTEN ON SOCKET
while True:
    msg = my_server_socket.recv()
    if msg == b'Hi':
        to_client = b"Well hello to you"
        my_server_socket.send(to_client)
    else:
        to_client = b"Not sure what you want"
        my_server_socket.send(to_client)
Washroom answered 16/9, 2015 at 18:6 Comment(0)
K
3

ZeroMQ supports .poll() to non-blocking test before .recv()

One can use .poll()

.poll( timeout = None, flags = zmq.POLLIN ) # poll the socket for events

The default is to poll forever for incoming events. Timeout is in milliseconds, if specified.

Parameters:

timeout : int [default: None]

The timeout ( in milliseconds ) to wait for an event. If unspecified (or specified None), will wait forever for an event.

flags : bitfield (int) [default: POLLIN]

The event flags to poll for ( any combination of POLLIN | POLLOUT ). The default is to check for incoming events ( POLLIN ).

Returns:

events : bitfield (int)

The events that are ready and waiting. Will be 0 if no events were ready by the time timeout was reached.

ZeroMQ supports non-blocking, asynchronous mode for .recv()

so may build one's own, non-blocking, soft-RT-tuned .recv() busy loop.

while not_SIG_KILL_yet:     # main-<LOOP> -<o>-<o>-<o>-<o>-<o>-<o>-<o>-<o>-<o>-
    try:                                         # TRY: an-outer-most-<ExceptionWRAPPER> for KeyboardInterrupt
         ''' ............................................................ 250 msec sample-rate <loop>-task ____________________________________________________________________________'''
         try:
             maybeRECV = my_client_socket.recv( zmq.NOBLOCK )

             # Handle .recv() data

         except:
             # Handle ZMQError EAGAIN

             # .INC failed attempts COUNTER

             # .IF >
             if ( COUNTER > aTresholdToGiveUp ):
                not_SIG_KILL_yet = False
                continue

             # GIVE CPU-a-NAP -------------------- may be segmented + EXC-handler

             # ------------------------------------------------------------------
    except KeyboardInterrupt:
        not_SIG_KILL_yet = False

    pass
    # <EoW>-----------------# main-<LOOP> -<o>-<o>-<o>-<o>-<o>-<o>-<o>-<o>-<o>-

ZeroMQ works on Archetype Pattern, not on "dumb"-socket

Thus being afraid to meet "old"-[REP]-answer ( still hanging ( and it must hang there, mustn't it? ) on the SERVER-side in the internal Queue ) is correct as the REQ/REP-pattern is exactly doing that by-definition.

The CLIENT side has the right to gracefully close the associated resources and send SERVER an indication of clearing the circus.

ZeroMQ support very dynamic set-up/tear-downs of the ground elements and it is a fair manner not to leave communication-counterparty ( SERVER in this case ) in any doubts about what the communication-peer intended to do.

Read details about:

my_client_context.setsockopt( zmq.LINGER, 0 ) # do not wait for anything
my_client_socket.close()                      # stateful close
my_client_context.term()                      # graceful termination / release of resources ( no MEM leaks )
Keith answered 17/9, 2015 at 13:24 Comment(4)
This is perfect. I'm using .poll() to poll the socket for a message. I register the poller using zpoller_in.register(my_client_socket, zmq.POLLIN). Then I monitor the event (to see if its empty) up to 2 seconds zpoller_in_events = zpoller_in.poll(2000).Washroom
@JeffDeCola Cool to know you enjoy the Answer in practical use. Would you consider [+1] and accepting the answer? + one recent joke :o) ... type "fed chair" into google search (without quotes & no autocomplete offerings ) and look what their AI engine produces (rofl)Keith
I need a few more exp points before i can +1. But I will when i can. Thanks again!Washroom
:o) funny system, isn't it?Keith

© 2022 - 2024 — McMap. All rights reserved.