pyzmq recv_json无法解码send_json发送的消息

时间:2015-12-12 16:41:39

标签: zeromq pyzmq

这是我的代码,其中删除了无关的内容:

coordinator.py

context = zmq.Context()
socket = context.socket(zmq.ROUTER)
port = socket.bind_to_random_port(ZMQ_ADDRESS)

poller = zmq.Poller()
poller.register(socket, zmq.POLLIN)

while True:
    event = poller.poll(1)
    if not event:
        continue
    process_id, val = socket.recv_json()

worker.py

context = zmq.Context()
socket = context.socket(zmq.DEALER)
socket.connect('%s:%s' % (ZMQ_ADDRESS, kwargs['zmq_port']))

socket.send_json(
    (os.getpid(), True)
)

运行时会发生什么:

    process_id, val = socket.recv_json()
  File "/Users/anentropic/.virtualenvs/myproj/lib/python2.7/site-packages/zmq/sugar/socket.py", line 380, in recv_json
    return jsonapi.loads(msg)
  File "/Users/anentropic/.virtualenvs/myproj/lib/python2.7/site-packages/zmq/utils/jsonapi.py", line 71, in loads
    return jsonmod.loads(s, **kwargs)
  File "/Users/anentropic/.virtualenvs/myproj/lib/python2.7/site-packages/simplejson/__init__.py", line 451, in loads
    return _default_decoder.decode(s)
  File "/Users/anentropic/.virtualenvs/myproj/lib/python2.7/site-packages/simplejson/decoder.py", line 406, in decode
    obj, end = self.raw_decode(s)
  File "/Users/anentropic/.virtualenvs/myproj/lib/python2.7/site-packages/simplejson/decoder.py", line 426, in raw_decode
    raise JSONDecodeError("No JSON object could be decoded", s, idx)
JSONDecodeError: No JSON object could be decoded: line 1 column 0 (char 0)

如果我用ipdb挖掘:

> /Users/anentropic/.virtualenvs/myproj/lib/python2.7/site-packages/zmq/sugar/socket.py(380)recv_json()
    379             msg = self.recv(flags)
--> 380             return jsonapi.loads(msg)
    381

ipdb> p msg
'\x00\x9f\xd9\x06\xa2'
嗯,这看起来不像JSON ......这是pyzmq中的一个错误吗?我用错了吗?

1 个答案:

答案 0 :(得分:4)

嗯,好的,找到了答案。

ØMQ界面存在恼人的不对称性,因此您必须了解正在使用的套接字类型。

在这种情况下,我使用ROUTER / DEALER体系结构意味着当我执行send_json时,从DEALER套接字发送的JSON消息将包含在 multipart 消息信封中。第一部分是客户端ID(我猜这是我上面提到的'\x00\x9f\xd9\x06\xa2'),第二部分是我们感兴趣的JSON字符串。

所以在我的coordinator.py的最后一行我需要这样做:

id_, msg = socket.recv_multipart()
process_id, val = json.loads(msg)

恕我直言这是ØMQ/ pyzmq的糟糕设计,图书馆应该抽象出来并且只有sendrecv方法才能正常工作。

我从这个问题How can I use send_json with pyzmq PUB SUB得到了线索,所以看起来PUB / SUB架构有同样的问题,毫无疑问其他问题。

这在文档中有描述,但不是很清楚 http://zguide.zeromq.org/page:all#The-Asynchronous-Client-Server-Pattern

<强>更新

事实上,我发现在我的情况下,我可以通过直接使用消息信封的“客户端ID”部分来进一步简化代码。所以工人就是这样做的:

context = zmq.Context()
socket = context.socket(zmq.DEALER)
socket.identity = str(os.getpid())  # or I could omit this and use ØMQ client id
socket.connect('%s:%s' % (ZMQ_ADDRESS, kwargs['zmq_port']))

socket.send_json(True)

值得注意的是,当您想要从ROUTER发送另一个方向的消息时,您必须将其作为multipart发送,指定它的目的地,例如:

coordinator.py

context = zmq.Context()
socket = context.socket(zmq.ROUTER)
port = socket.bind_to_random_port(ZMQ_ADDRESS)

poller = zmq.Poller()
poller.register(socket, zmq.POLLIN)

pids = set()
while True:
    event = poller.poll(1)
    if not event:
        continue
    process_id, val = socket.recv_json()
    pids.add(process_id)

    # need some code in here to decide when to stop listening
    # and break the loop

for pid in pids:
    socket.send_multipart([pid, 'a string message'])
    # ^ do your own json encoding if required

我猜可能有一些ØMQ方式来做广播消息,而不是像上面那样在循环中发送给每个客户端。我希望文档只清楚地描述每种可用的套接字类型以及如何使用它们。