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)
.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.
.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>-
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 )