Why is gevent.sleep(0.1) necessary in this example to prevent the app from blocking?
Asked Answered
S

1

7

I'm pulling my hair out over this one. I'm trying to get the simplest of examples working with zeromq and gevent. I changed this script to use PUB/SUB sockets and when I run it the 'server' socket loops forever. If I uncomment the gevent.sleep(0.1) line then it works as expected and yields to the other green thread, which in this case is the client.

The problem is, why should I have to manually add a sleep call? I thought when I import the zmq.green version of zmq that the send and receive calls are non blocking and underneath do the task switching.

In other words, why should I have to add the gevent.sleep() call to get this example working? In Jeff Lindsey's original example, he's doing REQ/REP sockets and he doesn't need to add sleep calls...but when I changed this to PUB/SUB I need it there for this to yield to the client for processing.

#Notes: Code taken from slide: http://www.google.com/url?sa=t&rct=j&q=zeromq%20gevent&source=web&cd=27&ved=0CFsQFjAGOBQ&url=https%3A%2F%2Fraw.github.com%2Fstrangeloop%2F2011-slides%2Fmaster%2FLindsay-DistributedGeventZmq.pdf&ei=JoDNUO6OIePQiwK8noHQBg&usg=AFQjCNFa5g9ZliRVoN_yVH7aizU_fDMtfw&bvm=bv.1355325884,d.cGE
#Jeff Lindsey talk on gevent and zeromq

import gevent
from gevent import spawn
import zmq.green as zmq

context = zmq.Context()

def serve():
    print 'server online'
    socket = context.socket(zmq.PUB)
    socket.bind("ipc:///tmp/jeff")
    while True:
        print 'send'
        socket.send("World")
        #gevent.sleep(0.1)

def client():
    print 'client online'
    socket = context.socket(zmq.SUB)
    socket.connect("ipc:///tmp/jeff")
    socket.setsockopt(zmq.SUBSCRIBE, '') 
    while True:
        print 'recv'
        message = socket.recv()


cl = spawn(client)
server = spawn(serve)

print 'joinall'
gevent.joinall([cl, server])


print 'end'
Saltsman answered 16/12, 2012 at 18:12 Comment(0)
H
7

I thought when I import the zmq.green version of zmq that the send and receive calls are non blocking and underneath do the task switching.

zmq.green will only yield if these calls would block, it does not yield if they are ready (there's nothing to wait for). In your case the sender is always ready, so it never has a reason to yield.

Some pointers:

  • a minimal explicit yield is gevent.sleep(0), it doesn't need to be finite.
  • zmq.green only yields on blocking calls. That is, if a socket is always ready to send/recv when you ask it to, it will never yield.
  • socket.send only blocks when the socket is not ready to send (not (socket.events & zmq.POLLOUT)), which can never actually be true of a PUB socket (you will see it at HWM for PUSH, DEALER, etc.).
  • in general, don't trust send to yield, because of the way zeromq works this will rarely be the case unless you are exceeding the capacity of your configuration.
  • unlike send, recv regularly blocks in normal usage, so it yields on most calls. But if a peer is flooding your incoming buffer, repeated recv calls will not yield until there is nothing ready to receive, so you may again need to explicitly yield every so often to prevent starvation.

What zmq.green amounts to is turning send/recv into:

try:
    socket.send(msg, zmq.NOBLOCK) # or recv
except zmq.ZMQError as e:
    if e.errno == zmq.EAGAIN:
        yield # and wait for socket to be ready, then try again

so if send/recv with NOBLOCK are always succeeding, the socket never yields.

To put it another way: If a socket has nothing to wait for, it won't wait.

Hearsay answered 16/12, 2012 at 21:38 Comment(2)
Thanks for the details minrk. I'm really trying to understand how gevent and zeromq work together. Honestly, I've been struggling to wrap my head around gevent and coroutines in general but while I feel this combination of gevent and zeromq is amazingly powerful I can't but feel like using gevent requires a thoroughly deep understanding of the internals of the socket api and how it's setup in code. Is there any good books or documentation you can recommend? Specifically on these two technologies?Saltsman
Not that I am aware of. I am actually not too fond of gevent, for reasons like this - too much magic for my taste. I understand this particular case because I helped the author of zmq's gevent support figure out a weird behavior that ultimately turned out to be a gevent bug. For zeromq itself, the guide is always the place to start.Hearsay

© 2022 - 2024 — McMap. All rights reserved.