简体   繁体   English

为什么 pyzmq 订阅者与 asyncio 的行为不同?

[英]Why does pyzmq subscriber behave differently with asyncio?

I have an XPUB/XSUB device and a number of mock publishers running in one process.我有一个 XPUB/XSUB 设备和许多模拟发布者在一个进程中运行。 In a separate process, I want to connect a subscriber and print received message to the terminal.在一个单独的过程中,我想连接一个订阅者并将收到的消息打印到终端。 Below I will show two variants of a simple function to do just that.下面我将展示一个简单函数的两个变体来做到这一点。 I have these functions wrapped as command-line utilities.我将这些函数包装为命令行实用程序。

My problem is that the asyncio variant never receives messages .我的问题是 asyncio 变体永远不会收到消息

On the other hand, the non-async variant works just fine.另一方面,非异步变体工作得很好。 I have tested all cases for ipc and tcp transports.我已经测试了 ipc 和 tcp 传输的所有情况。 The publishing process never changes in my tests, except when I restart it to change transport.发布过程在我的测试中从未改变,除非我重新启动它以更改传输。 The messages are short strings and published roughly once per second, so we're not looking at performance problem.消息是短字符串,大约每秒发布一次,因此我们不考虑性能问题。

The subscriber program sits indefinitely at the line msg = await sock.receive_multipart() .订阅者程序无限期地位于msg = await sock.receive_multipart() In the XPUB/XSUB device I have instrumentation that shows the forwarding of the sock.setsockopt(zmq.SUBSCRIBE, channel.encode()) message, same as when the non-async variant connects.在 XPUB/XSUB 设备中,我有仪表显示sock.setsockopt(zmq.SUBSCRIBE, channel.encode())消息的转发,与非异步变体连接时相同。

The asyncio variant (not working, as described) asyncio 变体(不起作用,如上所述)

def subs(url, channel):
    import asyncio

    import zmq
    import zmq.asyncio

    ctx = zmq.asyncio.Context.instance()
    sock = ctx.socket(zmq.SUB)
    sock.connect(url)
    sock.setsockopt(zmq.SUBSCRIBE, channel.encode())

    async def task():
        while True:
            msg = await sock.recv_multipart()
            print(' | '.join(m.decode() for m in msg))

    try:
        asyncio.run(task())
    finally:
        sock.setsockopt(zmq.LINGER, 0)
        sock.close()

The regular blocking variant (works fine)常规阻塞变体(工作正常)

def subs(url, channel):
    import zmq

    ctx = zmq.Context.instance()
    sock = ctx.socket(zmq.SUB)
    sock.connect(url)
    sock.setsockopt(zmq.SUBSCRIBE, channel.encode())

    def task():
        while True:
            msg = sock.recv_multipart()
            print(' | '.join(m.decode() for m in msg))

    try:
        task()
    finally:
        sock.setsockopt(zmq.LINGER, 0)
        sock.close()

For this particular tool there is no need to use asyncio.对于这个特定的工具,不需要使用 asyncio。 However, I am experiencing this problem elsewhere in my code too, where an asynchronous recv never receives.但是,我在代码的其他地方也遇到了这个问题,异步接收从未收到。 So I'm hoping that by clearing it up in this simple case I'll understand what's going wrong in general.所以我希望通过在这个简单的案例中清理它,我会理解一般出了什么问题。

My versions are我的版本是

import zmq
zmq.zmq_version()  # '4.3.2'
zmq.__version__  # '19.0.2'

I'm on MacOS 10.13.6.我在 MacOS 10.13.6 上。

I'm fully out of ideas.我完全没有想法。 Internet, please help!互联网,请帮助!

A working async variant is一个工作异步变体是

def subs(url, channel):
    import asyncio

    import zmq
    import zmq.asyncio

    ctx = zmq.asyncio.Context.instance()

    async def task():
        sock = ctx.socket(zmq.SUB)
        sock.connect(url)
        sock.setsockopt(zmq.SUBSCRIBE, channel.encode())

        try:
            while True:
                msg = await sock.recv_multipart()
                print(' | '.join(m.decode() for m in msg))
        finally:
            sock.setsockopt(zmq.LINGER, 0)
            sock.close()

    asyncio.run(task())

I conclude that, when using asyncio zmq, sockets must be created with a call running on the event loop from which the sockets will be awaited.我得出的结论是,在使用 asyncio zmq 时,必须通过在等待套接字的事件循环上运行的调用来创建套接字。 Even though the original form did not do anything fancy with event loops, it appears that the socket has an event loop different from that used by asyncio.run .尽管原始表单没有对事件循环做任何花哨的事情,但套接字似乎有一个与asyncio.run使用的事件循环不同的事件循环。 I'm not sure why, and I didn't open an issue with pyzmq because their docs show usage as in this answer, without comment.我不知道为什么,我没有打开 pyzmq 的问题,因为他们的文档显示了这个答案中的用法,没有评论。

Edit in response to a comment:编辑回复评论:

asyncio.run always creates a new event loop, so the loop presumably created for the sockets instantiated outside of the co-routine passed to asyncio.run (as in the asyncio variant in the original question) is obviously different. asyncio.run总是创建一个新的事件循环,因此可能为传递给asyncio.run的协同例程之外实例化的套接字创建的循环(如原始问题中的 asyncio 变体)显然不同。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM