【问题标题】:Asynchronous IPC between Node.js/Electron and PythonNode.js/Electron 和 Python 之间的异步 IPC
【发布时间】:2023-03-29 16:50:02
【问题描述】:

我尝试使用 Electron 为给定的 Python 代码构建 GUI。 数据流实际上是直截了当的:用户与 Electron 应用程序交互,后者向 Python API 发送请求,Python API 处理请求并发送回复。

到目前为止,一切都很好。我阅读了不同的主题和博客文章:

  1. ZeroRPC 解决方案:
  1. 从 node.js 生成 Python API 作为子进程并直接通信:
  1. 使用 zeroMQ 套接字(例如独占对?)

但是在所有三个解决方案中,我都在同一点上苦苦挣扎:我必须发出异步请求/回复,因为请求处理可能需要一些时间,而在这段时间内,可能已经发生了进一步的请求。对我来说,这看起来是一种非常常见的模式,但我在 SO 上没有找到任何东西,也许我只是不知道,我到底在寻找什么。

 Frontend                         Backend

     |                              |
REQ1 |—————————————————————————————>|Process REQ1——--
     |                              |               |
REQ2 |—————————————————————————————>|Process REQ2 --|----—
     |                              |               |    |
REP1 |<————————————————————————————-|REPLY1 <———————     | 
     |                              |                    |
REP2 |<————————————————————————————-|REPLY2 <———————————--
     |                              |

在我看来,最灵活的解决方案是 3. zeroMQ,但在 websitePython doc 上,我发现只有最小的工作示例,发送和接收都阻塞。

谁能给我一个提示?

【问题讨论】:

    标签: python sockets electron ipc zeromq


    【解决方案1】:

    如果您正在考虑使用 ZeroMQ,那么您正在进入 Actor 模型编程的世界。在 Actor 模型编程中,发送消息与接收消息无关(这两个活动是异步的)。

    ZeroMQ 的阻塞是什么意思

    当 ZeroMQ 谈到发送“阻塞”时,这意味着 ZeroMQ 用于在传输之前对消息进行排队的内部缓冲区已满,因此它会阻塞发送应用程序,直到该队列中有可用空间。清空队列是将较早的消息成功传输到接收器,接收器有一个接收缓冲区,必须由接收应用程序清空。实际传输消息的是属于 ZeroMQ 上下文的管理线程。

    这个管理线程是关键部分;它独立于您自己的应用程序线程运行,因此它使发送方和接收方之间的通信异步。

    您可能想要的是使用 ZeroMQ 的反应器 zmq_poll()。通常在 Actor 模型编程中,您有一个循环,顶部是对反应器的调用(在本例中为 zmq_poll())。 Zmq_poll() 会告诉您何时发生了某事,但在这里您主要感兴趣的是它告诉您消息已到达。通常,您会阅读该消息,对其进行处理(这可能涉及发送其他 ZeroMQ 消息),然后循环回 zmq_poll()。

    后端

    所以你的后端会是这样的:

    while (forever)
    {
        zmq_poll(list of input sockets) // allows serving more than one socket
        zmq_recv(socket that has a message ready to read) // will always succeed immediately because zmq_poll() told us there was a message waiting
        decode req message
        generate reply message
        zmq_send(reply to original requester) // Socket should be in blocking mode to ensue that messages don't get lost if something is unexpectedly running slowly
    }
    

    如果你不想服务多个前端,那就更简单了:

    while (forever)
    {
        zmq_recv(req)  // Socket should be in blocking mode
        decode req message
        generate reply message
        zmq_send(reply) // Socket should also be in blocking mode to ensure that messages don't get lost if something is unexpectedly running slow
    }
    

    前端

    您的前端会有所不同。基本上,您需要 Electron 事件循环处理程序来接管 zmq_poll() 的角色。在 Electron 中使用的 ZeroMQ 构建将解决这个问题。但基本上它将归结为发送 ZeroMQ 消息的 GUI 事件回调。当消息从后端到达套接字时,您还必须为 Electron 编写回调以运行。在发送和接收消息之间不会有前端的阻塞。

    时机

    这意味着你绘制的时序图是错误的。前端可以发送任意数量的请求,但是这些请求在后端的离开和到达之间没有时间对齐(尽管假设一切运行顺利,第一个请求将很快到达)。发送了一个或多个请求后,前端简单地返回做它想做的任何事情(对于用户界面,这通常只是等待事件的事件循环管理器)。

    该后端将处于读取/处理/回复、读取/处理/回复的循环中,一次处理一个请求。同样,那些离开和随后到达前端的回复之间没有时间对齐。当回复确实返回到前端时,它会唤醒并处理它。

    【讨论】:

    • 感谢您提供如此详细的回答!这有助于我思考基本设计。最初,我计划在单独的线程中执行generate reply message,这将导致后端对齐。但我发现这与 Actor 模型完全矛盾,Actor 模型旨在摆脱传统线程。
    猜你喜欢
    • 2022-08-08
    • 2021-05-13
    • 1970-01-01
    • 2010-12-14
    • 2017-09-15
    • 1970-01-01
    • 2011-03-22
    • 1970-01-01
    • 2013-04-13
    相关资源
    最近更新 更多