Почему gevent.sleep(0.1) необходим в этом примере для предотвращения блокировки приложения?

Я рву на себе волосы из-за этого. Я пытаюсь получить простейшие примеры работы с zeromq и gevent. Я изменил этот сценарий, чтобы использовать сокеты PUB/SUB, и когда я запускаю его, сокеты «сервера» зацикливаются навсегда. Если я раскомментирую строку gevent.sleep(0.1), она будет работать, как и ожидалось, и уступит место другому зеленому потоку, которым в данном случае является клиент.

Проблема в том, почему я должен вручную добавлять спящий вызов? Когда я импортирую версию zmq zmq.green, я думал, что вызовы отправки и получения не блокируются, а под ними выполняется переключение задач.

Другими словами, зачем мне добавлять вызов gevent.sleep(), чтобы этот пример заработал? В исходном примере Джеффа Линдси он выполняет сокеты REQ/REP, и ему не нужно добавлять вызовы сна... но когда я изменил это на PUB/SUB, мне нужно, чтобы это было передано клиенту для обработки.

#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'

person Ralph Caraveo    schedule 16.12.2012    source источник


Ответы (1)


Когда я импортирую версию zmq zmq.green, я думал, что вызовы отправки и получения не блокируются, а под ними выполняется переключение задач.

zmq.green будет уступать только в том случае, если эти вызовы будут заблокированы, он не уступает, если они готовы (нечего ждать). В вашем случае отправитель всегда готов, поэтому у него никогда нет причин уступать.

Некоторые указатели:

  • минимальный явный выход равен gevent.sleep(0), он не обязательно должен быть конечным.
  • zmq.green срабатывает только при блокировке вызовов. То есть, если сокет всегда готов к отправке/получению, когда вы его об этом просите, он никогда не уступит.
  • socket.send блокируется только тогда, когда сокет не готов к отправке (not (socket.events & zmq.POLLOUT)), что никогда не может быть правдой для сокета PUB (вы увидите это в HWM для PUSH, DEALER и т. д.).
  • в общем, не доверяйте send to yield, из-за того, как работает zeromq, это будет редко иметь место, если только вы не превысите возможности своей конфигурации.
  • в отличие от отправки, recv регулярно блокируется при обычном использовании, поэтому он уступает при большинстве вызовов. Но если одноранговый узел заполняет ваш входящий буфер, повторные вызовы recv не будут выполняться до тех пор, пока ничего не будет готово к приему, поэтому вам может снова понадобиться время от времени явно возвращать, чтобы предотвратить голодание.

zmq.green превращает send/recv в:

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

поэтому, если отправка/получение с NOBLOCK всегда успешны, сокет никогда не уступает.

Другими словами: если сокету нечего ждать, он не будет ждать.

person minrk    schedule 16.12.2012
comment
Спасибо за подробности minrk. Я действительно пытаюсь понять, как gevent и zeromq работают вместе. Честно говоря, я изо всех сил пытался понять gevent и сопрограммы в целом, но, хотя я чувствую, что эта комбинация gevent и zeromq удивительно мощная, я не могу не чувствовать, что использование gevent требует тщательного глубокого понимания внутренностей сокета. API и как это настроить в коде. Есть ли хорошие книги или документация, которую вы можете порекомендовать? Конкретно по этим двум технологиям? - person Ralph Caraveo; 17.12.2012
comment
Не то, чтобы я знаю. На самом деле я не слишком люблю gevent по таким причинам - на мой вкус, слишком много волшебства. Я понимаю этот конкретный случай, потому что помог автору поддержки gevent zmq выяснить странное поведение, которое в конечном итоге оказалось ошибкой gevent. Для Zeromq руководство всегда является отправной точкой. - person minrk; 18.12.2012