Node.js/Electron 和 Python 之间的异步 IPC
Asynchronous IPC between Node.js/Electron and Python
我尝试使用 Electron 为给定的 Python 代码构建一个 GUI。
数据流实际上很简单:用户与 Electron 应用程序交互,它向 Python API 发送请求,处理请求并发送回复。
到目前为止,还不错。我阅读了不同的主题和博客文章:
- ZeroRPC 解决方案:
- https://medium.com/@abulka/electron-python-4e8c807bfa5e
- https://github.com/fyears/electron-python-example
- 从 node.js 生成 Python API 作为子进程并直接通信:
- https://www.ahmedbouchefra.com/connect-python-3-electron-nodejs-build-desktop-apps/
- 这对我来说似乎不是最聪明的解决方案,因为使用 zeroRPC 或 zeroMQ 可以更轻松地更改前端架构而无需触及后端代码。
- 使用 zeroMQ 套接字(例如独占对?)
但是在所有三个解决方案中,我都在同一个点上挣扎:我必须进行异步 requests/replies,因为请求处理可能需要一些时间,而在这段时间内,可能已经发生了进一步的请求。对我来说,这看起来是一个非常常见的模式,但我在 SO 上什么也没找到,也许我只是不知道,我到底在寻找什么。
Frontend Backend
| |
REQ1 |—————————————————————————————>|Process REQ1——--
| | |
REQ2 |—————————————————————————————>|Process REQ2 --|----—
| | | |
REP1 |<————————————————————————————-|REPLY1 <——————— |
| | |
REP2 |<————————————————————————————-|REPLY2 <———————————--
| |
在我看来,最灵活的解决方案是 3.zeroMQ,但在 website and the Python doc 上,我只找到了最少的工作示例,其中发送和接收都是阻塞的。
有人可以给我提示吗?
如果您正在考虑使用 ZeroMQ,那么您就进入了 Actor 模型编程的世界。在 actor 模型编程中,发送消息独立于接收消息(这两个活动是异步的)。
ZeroMQ 中阻塞的含义
当 ZeroMQ 谈论发送 "blocking" 时,这意味着 ZeroMQ 用于在传输之前排队消息的内部缓冲区已满,因此它会阻塞发送应用程序,直到 space 在此队列中可用。清空队列的事情是将较早的消息成功传输到接收方,该接收方具有接收缓冲区,必须由接收应用程序清空。实际传输消息的是属于 ZeroMQ contenxt 的管理线程。
这个管理线程是关键部分;它 运行 独立于您自己的应用程序线程,因此它使发送方和接收方之间的通信异步。
您可能想要使用 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 编写回调 运行 。前端在发送和接收消息之间不会有任何阻塞。
时机
这意味着你画的时序图是错误的。前端可以根据需要发送任意数量的请求,但是这些请求离开和到达后端之间没有时间对齐(尽管假设一切都 运行ning 顺利,第一个几乎会立即到达) .发送一个或多个请求后,前端只需 returns 做任何它想做的事(对于用户界面,这通常只是等待事件的事件循环管理器)。
该后端将处于 read/process/reply、read/process/reply 循环中,一次处理一个请求。同样,那些离开和随后到达前端的回复之间没有时间对齐。当回复确实返回前端时,它会醒来并处理它。
我尝试使用 Electron 为给定的 Python 代码构建一个 GUI。 数据流实际上很简单:用户与 Electron 应用程序交互,它向 Python API 发送请求,处理请求并发送回复。
到目前为止,还不错。我阅读了不同的主题和博客文章:
- ZeroRPC 解决方案:
- https://medium.com/@abulka/electron-python-4e8c807bfa5e
- https://github.com/fyears/electron-python-example
- 从 node.js 生成 Python API 作为子进程并直接通信:
- https://www.ahmedbouchefra.com/connect-python-3-electron-nodejs-build-desktop-apps/
- 这对我来说似乎不是最聪明的解决方案,因为使用 zeroRPC 或 zeroMQ 可以更轻松地更改前端架构而无需触及后端代码。
- 使用 zeroMQ 套接字(例如独占对?)
但是在所有三个解决方案中,我都在同一个点上挣扎:我必须进行异步 requests/replies,因为请求处理可能需要一些时间,而在这段时间内,可能已经发生了进一步的请求。对我来说,这看起来是一个非常常见的模式,但我在 SO 上什么也没找到,也许我只是不知道,我到底在寻找什么。
Frontend Backend
| |
REQ1 |—————————————————————————————>|Process REQ1——--
| | |
REQ2 |—————————————————————————————>|Process REQ2 --|----—
| | | |
REP1 |<————————————————————————————-|REPLY1 <——————— |
| | |
REP2 |<————————————————————————————-|REPLY2 <———————————--
| |
在我看来,最灵活的解决方案是 3.zeroMQ,但在 website and the Python doc 上,我只找到了最少的工作示例,其中发送和接收都是阻塞的。
有人可以给我提示吗?
如果您正在考虑使用 ZeroMQ,那么您就进入了 Actor 模型编程的世界。在 actor 模型编程中,发送消息独立于接收消息(这两个活动是异步的)。
ZeroMQ 中阻塞的含义
当 ZeroMQ 谈论发送 "blocking" 时,这意味着 ZeroMQ 用于在传输之前排队消息的内部缓冲区已满,因此它会阻塞发送应用程序,直到 space 在此队列中可用。清空队列的事情是将较早的消息成功传输到接收方,该接收方具有接收缓冲区,必须由接收应用程序清空。实际传输消息的是属于 ZeroMQ contenxt 的管理线程。
这个管理线程是关键部分;它 运行 独立于您自己的应用程序线程,因此它使发送方和接收方之间的通信异步。
您可能想要使用 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 编写回调 运行 。前端在发送和接收消息之间不会有任何阻塞。
时机
这意味着你画的时序图是错误的。前端可以根据需要发送任意数量的请求,但是这些请求离开和到达后端之间没有时间对齐(尽管假设一切都 运行ning 顺利,第一个几乎会立即到达) .发送一个或多个请求后,前端只需 returns 做任何它想做的事(对于用户界面,这通常只是等待事件的事件循环管理器)。
该后端将处于 read/process/reply、read/process/reply 循环中,一次处理一个请求。同样,那些离开和随后到达前端的回复之间没有时间对齐。当回复确实返回前端时,它会醒来并处理它。