V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
justou
V2EX  ›  程序员

关于 ZeroMQ 的若干问题

  •  
  •   justou · 2023-01-14 22:57:40 +08:00 · 2647 次点击
    这是一个创建于 682 天前的主题,其中的信息可能已经有所发展或是发生改变。

    背景问题

    最近在搬一些把 Python 解释器嵌入到 C++中砖( Windows 下),虽然能搬,但并不是什么特别愉快的体验:要屏蔽应用对 Python 的显式依赖,所以通过 C 接口在 C++和 Python 之间传递数据和错误信息,C 接口下面又用 Cython 来进行数据转换和函数调用,基本上是这样的:

    C++ ⇌ C ⇌ Cython ⇌ Python

    还有个麻烦是这个过程中 C++,Cython ,Python 没法一起 debug ( C++用的 CLion 开发,Cython 和 Python 用的 PyCharm ),而且 Cython 的 debug 只能进入到生成的 cpp 中,不过还好,能定位问题在哪个 Cython 函数,很多时候只能在 Cython 中加 print 来看传递的参数 (如果有更好的办法请教教我 o(╥﹏╥)o)

    总之,坑,特别多的坑,即使用了 Cython 不直接用 Python 的 C API 也要写不少代码,而且 Python 解释器在应用进程,如果没照顾好它在运行中的情绪,轻则自己崩掉,重则一起崩掉,不管哪种情况应用基本要重启了。

    新的解决方案

    不清楚什么时候在哪儿看到过 RPC(Remote Procedure Call)的概念, 然后经过一番谷噜,看到了这篇博客 https://opensource.com/article/20/3/zeromq-c-python 作者开头提到的困境跟我现在经历的差不多(其实给 Python 写扩展还好啦,Python 生态就是这样来的,反过来把 Python 当成库用才更头疼)。作者提到 ZeroMQ 是这种问题的解决方案( ZeroMQ 我是在 V2 看到过相关讨论的,虽然当时可能并不清楚这东西多有用)。经过翻阅一些文档看了一些例子,我对以上背景问题也有了以下基于 ZeroMQ 的设想方案:

    C++中启动一个 Python 脚本,该脚本运行在独立进程中作为一个服务等待请求; C++通过发送请求传递需要处理的参数给 Python ,Python 处理后把结果发送回去;要是传递的参数不合适导致服务崩了,没关系,重启该 Python 脚本继续服务,修改参数再传(如果是 Python 解释器嵌入 C++则只能启动一次关闭一次,无法重新启动);完事儿后 C++可以传递特殊参数优雅地停止该服务。而且开发时各个模块我都可以在各自的开发环境中 debug 了╰(°▽°)╯!

    有了这种方式后的确打开了编写应用的新世界大门(即使仅仅是本地的桌面应用),用合适的语言和工具编写独立的功能模块,然后通过 ZeroMQ 将各个独立模块连接起来构成整个系统。

    前景是美好的,但是实践起来肯定有坑,所以想请熟知 ZeroMQ 并用其在应用中摸爬滚打过的前辈们现身说法,谈谈自己对 ZeroMQ 看法,分享一些观点和经验,踩过的坑,缺点以及克服方法。

    关于 ZeroMQ 的若干问题

    1. Hintjens 大神的这份遗产 https://zguide.zeromq.org/ 主要讲了 HowTo ,有没有讲解底层原理的一些资料呢?
    2. C 源码暂时没有时间和精力去啃,缺乏一些关键的背景知识也啃不动,所以需要哪些背景知识才能很好的理解其中的各种概念呢?
    3. 想了解使用 ZeroMQ 各种 pattern 时底层究竟发生了什么?数据是怎么从一个地方传到另一个地方的?

    在我的观念里至少要了解到某个工具的一些基本的底层原理才能更好的使用该工具。ZeroMQ 是一个值得深入了解的工具。

    明天调休继续上班,到时再来看各位的回复

    第 1 条附言  ·  2023-01-15 10:06:23 +08:00
    RPC 方案:
    gRPC 啥都封装好了
    ZeroMQ + Protocol Buffers (既然用 Protocol Buffers 不如直接 gRPC 吧)
    ZeroMQ + MessagePack (轻量级,但要自己实现消息传递和编码解码信息)
    ZeroRPC (ZeroMQ + MessagePack 的现成封装,只有 python nodejs 的实现)
    第 2 条附言  ·  2023-01-16 10:33:00 +08:00
    14 条回复    2023-01-16 14:02:33 +08:00
    roundgis
        1
    roundgis  
       2023-01-14 23:32:56 +08:00 via Android   ❤️ 1
    jetbrains rider 就是這樣做的

    ui 用 java

    啟動的時候會同時啟動一個.net backend 進程

    兩者用 socket 通信

    我之前有一個應用也用了類似的模式

    ui c#

    backend golang
    ysc3839
        2
    ysc3839  
       2023-01-15 01:05:51 +08:00 via Android
    关于原始问题,你是想用 Python 给 C++应用写插件吗?一般的做法是原应用暴露出一个 C 的插件接口,然后用 C/C++写个 DLL 对接 Python ,图省事可以用 pybind11 ,不麻烦,用 Cython 感觉更复杂了。

    不过上述方案确实有不便于调试的问题,RPC 也是另一种方案。不过我个人不推荐用 ZeroMQ 来实现 RPC ,它本身不是针对 RPC 设计的,还需要你额外补充一些东西才能成为完整的 RPC 框架。可以考虑一下 grpc 。
    xsen
        3
    xsen  
       2023-01-15 07:24:31 +08:00   ❤️ 1
    真心建议不要从无到由这样通过 zmq 造 rpc ( N 年前造过,要稳定可产品化——工作量还是不小的)
    grpc 或 nats 成熟的方案很香,都是用 proto 定义 protocol 的
    xgdgsc
        4
    xgdgsc  
       2023-01-15 08:32:48 +08:00 via Android   ❤️ 1
    初学还是用 grpc jsonrpc 把功能实现了吧,如果对延迟要求不高。另外考虑下是否内嵌是有必要的,能不能纯用 cpp 纯用 julia 纯用 rust 代替,学新语言可能都比折腾 py cpp 两个麻烦的玩意要省事
    justou
        5
    justou  
    OP
       2023-01-15 09:09:01 +08:00
    @ysc3839 是的, 就是一个 C ABI 的 DLL 将整个 python 虚拟机隐藏在下面, 这样上层应用不论是 C++ C#还是其它什么实现都能用了. Cython 主要在 cdef public 的函数中对 C++传过来的指针进行数据转换然后再直接调用 python 函数. 既然已经到 C ABI 层次了, 可能 pybind11 与 Cython 区别不大了, 主要是我对 Cython 熟悉一点, 其实开发中最麻烦的还是 debug 问题, 两边无法直接联动.

    @xsen @xgdgsc
    改用 zmq 替换嵌入 python 虚拟机具体一点的想法是这样的:
    还是一个 C ABI 的 DLL, 假设有图像处理和视频(几秒的视频)处理功能用 python 来实现, start_image_service, start_video_service 分别启动对应的 python 脚本在后台等待请求, process_image(void* image, ...), process_video(void* video, ...)将数据发送过去, 等待处理结果; python 接收到对应的字节后对其解码成适合自己的数据结构, 处理后发送回去.

    不过随着应用的通信变得复杂, 这种方式可能会变得不可控, 看来大家说的 gprc 才是正确选择; 对延迟要求还是挺高的, 所以进程间通信越快越好.

    其实我主要是想了解下 zmq 底层实现的一些思想, 也就是上面关于 ZeroMQ 的 1,2,3 个问题.
    xgdgsc
        6
    xgdgsc  
       2023-01-15 11:25:44 +08:00   ❤️ 1
    如果视频和图片数据可以用 memory map 方式共享的话,python 和 cpp 进程共享 mmap 后,zeromq 只用 reqrep 传递控制处理这个共享内存的指令,也是一种比较简单的通信方案. 延迟有要求最好不要 rpc. https://stackoverflow.com/a/69806149/1136027
    xsen
        7
    xsen  
       2023-01-15 12:28:22 +08:00   ❤️ 1
    @justou #5 底层就是 tcp/udp 。有些是 cs ,有些是 cs/cs (就是互为 server ),这个是跟不同的模式有关
    你之所以有疑惑,是没做过裸 socket 的编程,没做过 cs 架构的东西

    其实架构都是差不多的,1:N 的话,都是有个类似的 broker 做分发

    我们之前是 zmq + protobuf ,架构就是 cs 架构
    xsen
        8
    xsen  
       2023-01-15 12:30:23 +08:00
    若是通过算法做图片或视频流的处理,一般图片或视频流(因为就几秒,类似的小文件)都是通过 kafka 来做的
    因为消息队列来说,kakfa 吞吐量都是最大的。之前接触过一些做视频分析的,就是用 kafka
    xsen
        9
    xsen  
       2023-01-15 12:32:47 +08:00
    除非对性能或延迟要求极高,不然是不建议通过封装 c/c++库的方法与第三方语言集成的,不管是维护、扩展都不太灵活
    通过 mq 解耦是通用的做法
    oneisall8955
        10
    oneisall8955  
       2023-01-15 12:51:22 +08:00 via Android
    实习时候接触过,没整明白。印象只是个类库,按需组合成自己的消息中间件
    justou
        11
    justou  
    OP
       2023-01-15 13:26:38 +08:00
    @xgdgsc 感谢提供这个新思路.

    @xsen 的确以前没怎么接触网络编程, 所以了解到 zmq 后才一拍脑门: 这东西不仅可以让软件解耦成更加容易开发和维护的独立模块, 还直接导致硬件也跟着解耦了, 比如给一批老旧的硬件系统新增加速卡(GPU/TPU)计算服务, 不用去动原有的硬件结构, 甚至可能无法动, 只需要一根网线牵出来连到新买的加速卡服务器上, 旧系统上原有的软件也不需要太多的更改, 只需要调用新开发的接口程序, 把数据发送到加速卡上计算就行了.
    lolizeppelin
        12
    lolizeppelin  
       2023-01-15 15:54:46 +08:00
    python rpc 直接用 openstack 组件 oslo_message
    pppoe
        13
    pppoe  
       2023-01-16 00:44:52 +08:00
    我就是基于 zmq, 重构了项目代码, 进程内和进程间(网络)通讯都是 zmq 的. zmq 是我项目架构的灵魂
    BingoXuan
        14
    BingoXuan  
       2023-01-16 14:02:33 +08:00
    zmq 最重要是不用考虑连接问题。不管是 tcp 还是 udp 还是 unix socket ,zmq 底层都帮你做好。zmq 并不希望开发者花费大量工夫去了解底层。

    如果架构只用了 python 和 c++,你可以试一下用 pybind11 。底层序列化用 json 或者 msgpack 或者 flatbuffer 都可以,c++写业务上 entity 的 encode 和 decode ,生成 c++和 python 的库。无论谁是 server 或者 client 或者 p2p 都不怕
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1030 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 21:51 · PVG 05:51 · LAX 13:51 · JFK 16:51
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.