zeromq: сброс состояния сокета REQ/REP

когда вы используете простой шаблон ZeroMQ REQ / REP, вы зависите от фиксированной последовательности send()->recv() / recv()->send (). As этой статья описывает, что вы попадаете в беду, когда участник отключается в середине запроса, потому что тогда вы не можете просто начать с получения следующего запроса от другого соединения, но государственная машина заставит вас отправить запрос на отключенный.

появился более элегантный способ решить эту проблему, так как упомянутая статья была написана?

повторно подключается единственный способ решить эту проблему (кроме не использования REQ/REP, но использовать другой шаблон)

4 ответов


хорошей новостью является то, что, начиная с ZMQ 3.0 и позже (современная эра), вы можете установить тайм-аут на сокете. Как отмечали другие в другом месте, вы должны сделать это после создания сокета, но перед его подключением:

zmq_req_socket.setsockopt( zmq.RCVTIMEO, 500 ) # milliseconds

затем, когда вы фактически пытаетесь получить ответ (после того, как вы отправили сообщение в сокет REP), вы можете поймать ошибку, которая будет утверждена, если тайм-аут превышен:

 try:
   send( message, 0 )
   send_failed = False

 except zmq.Again:
   logging.warning( "Image send failed." )
   send_failed = True

однако! Когда это произойдет, как наблюдаемый в другом месте, ваш сокет будет в забавном состоянии, потому что он все равно будет ожидать ответа. На данный момент я не могу найти ничего, что работает надежно, кроме перезапуска сокета. Обратите внимание, что если вы отключите() сокет, а затем повторно подключите() его, он все равно будет в этом плохом состоянии. Таким образом, вам нужно

def reset_my_socket:
  zmq_req_socket.close()
  zmq_req_socket = zmq_context.socket( zmq.REQ )
  zmq_req_socket.setsockopt( zmq.RCVTIMEO, 500 ) # milliseconds
  zmq_req_socket.connect( zmq_endpoint )

вы также заметите, что, поскольку я закрываю () D сокет, параметр тайм-аута приема был "потерян", поэтому важно установить, что на новом разъем.

надеюсь, это поможет. И я надеюсь, что это не окажется самым лучшим ответом на этот вопрос. :)


поскольку принятый ответ кажется мне ужасно грустным, я провел некоторое исследование и обнаружил, что все, что нам нужно, было на самом деле в документации.

на .setsockopt() С правильным параметром может помочь вам сбросить состояние сокета-машина без жестоко уничтожить его и перестроить другой поверх предыдущего мертвого тела.

(да, мне нравится образ).

ZMQ_REQ_CORRELATE: матч с ответами запросы
Поведение по умолчанию REQ сокеты должны полагаться на порядок сообщений для соответствия запросам и ответам, и этого обычно достаточно. Если этот параметр имеет значение 1 на REQ сокет будет префиксом исходящих сообщений с дополнительным фреймом, содержащим запрос id. Это означает, что полное сообщение (request id, identity, 0, user frames…). The REQ сокет отбросит все входящие сообщения, которые не начинаются с этих двух кадры.
Тип значения параметра int
Значение параметра unit 0, 1
Значение по умолчанию 0
Применимые типы сокетов ZMQ_REQ

ZMQ_REQ_RELAXED: ослабьте строгое чередование между запросом и ответом
По умолчанию REQ сокет не позволяет инициировать новый запрос с помощью zmq_send(3) пока не будет получен ответ на предыдущий. Когда установлено значение 1 отправка другое сообщение разрешено и имеет эффект отключения базового соединения с одноранговым узлом, от которого ожидался ответ, вызывая попытку повторного подключения на транспортах, которые его поддерживают. Машина состояния запрос-ответ сбрасывается и новый запрос отправляется следующему доступному одноранговому узлу.
Если установлено значение 1, а также включить ZMQ_REQ_CORRELATE для обеспечения правильного соответствия запросов и ответов. В противном случае поздний ответ на прерванный запрос может быть сообщен как ответ на заменяющее запросу.
Тип значения параметра int
Значение параметра unit 0, 1
Значение по умолчанию 0
Применимые типы сокетов ZMQ_REQ

полная документация здесь


есть одно решение для этого, и это добавление таймаутов ко всем вызовам. Поскольку ZeroMQ сам по себе не обеспечивает простую функциональность таймаута, я рекомендую использовать подкласс сокета ZeroMQ, который добавляет параметр таймаута ко всем важным вызовам.

Итак, вместо вызова s.recv () вы бы назвали s.recv (timeout=5.0), и если ответ не вернется в течение этого 5-секундного окна, он не вернет None и прекратит блокировку. Я сделал тщетную попытку, когда бежал. в эту проблему.


Я на самом деле изучаю это на данный момент, потому что я ретро-подгонка устаревшей системы.

Я постоянно сталкиваюсь с кодом, который "должен" знать о состоянии соединения. Однако дело в том, что я хочу перейти к парадигме передачи сообщений, которую продвигает библиотека.

Я нашел следующую функцию : zmq_socket_monitor

что он делает, это контролировать сокет, переданный ему, и генерировать события, которые затем передаются конечная точка" inproc " - в этот момент Вы можете добавить код обработки, чтобы действительно что-то сделать.

здесь также есть пример (фактически тестовый код):github

У меня нет конкретного кода, чтобы дать на данный момент (возможно, в конце недели), но мое намерение состоит в том, чтобы ответить на подключение и разъединения, чтобы я мог фактически выполнить любой сброс логики требуется.

надеюсь, это поможет, и, несмотря на цитирование 4.2 docs, я использую 4.0.4 который, кажется, имеет функциональность также.

Примечание я заметил, что вы говорите о python выше, но вопрос помечен C++, поэтому мой ответ исходит от...