【问题标题】:Low-latency IPC between C++ and JavaC++ 和 Java 之间的低延迟 IPC
【发布时间】:2011-09-04 17:39:34
【问题描述】:

对于以下情况,实现 C++/Java IPC 的最佳方式是什么?

(最近有人问了similar question,不过我的要求比较具体)

  1. 我有两个程序——一个用 C++ 编写,另一个用 Java 编写——它们需要相互通信。两者都在同一台机器上运行。

  2. 程序相互发送消息。消息通常很短(少于几百字节),但可能有 100KB 或更大的大小。

  3. 消息不需要被确认(即,不是像 HTTP 这样的请求/响应模型)。例如,C++ 程序向 Java 程序发送一条消息,Java 程序可以稍后通过向 C++ 程序发送一条消息来回复——反之亦然。

  4. 理想的解决方案应具有 a) 非常低的延迟,b) 没有安全问题(用户不必授权打开端口等),c) 与平台无关。

我的第一个想法是使用 sockets——每个程序都将充当另一个程序的服务器。套接字比其他形式的 IPC 开销更大,如果我让系统自动分配端口号,我不知道服务器将如何通知客户端端口号。我也考虑过命名管道,但它们不受不同平台的支持(至少不一致)。 JNI 看起来像是一个选项,但它可以跨越进程边界吗?

有什么建议吗?

谢谢!

后续问题

  1. 如果我使用套接字,是否需要打开 两个 套接字以允许如上所述的异步通信?

【问题讨论】:

  • 在回答您的后续问题时 - 不,两种语言都支持异步 IO。

标签: java c++ sockets java-native-interface ipc


【解决方案1】:

另一种方法是使用嵌入式数据库(因为您正在考虑使用多个 IPC,我假设这两个应用程序都在同一台机器上)。

我以前在一个应用程序上工作,其中 c++ 应用程序从各种渠道获取数据并将其放入数据库(内存数据库;TimesTen)。为了向用户显示该数据,Java 应用程序将从 DB 中查询它。

对于您的使用,我不知道您是否愿意考虑使用 Oracle 的 Timesten,但您也可以使用 Berkeley 的嵌入式 DB。

【讨论】:

    【解决方案2】:

    使用 JNI 将允许访问系统中的所有可能性,而不仅仅是 Java 直接支持的那些;例如,如果您使用共享内存,这将是必要的。然而,JNI 本身相当昂贵。

    延迟问题很棘手,因为我所知道的机制都没有提供任何保证。总而言之,最快的可能是某种形式的共享内存,当数据存在时使用信号唤醒另一个进程。这将需要在 Java 端使用 JNI,但如果做得正确,可能仍然会提供最低的延迟——然而,正确地做(确保没有消息丢失)绝非易事。基于 Unix 的平台确实支持排队信号,并在单独的线程中将它们作为事件处理;我不了解 Windows。

    除此之外,命名管道通常非常有效;延迟可以与共享内存一样好,但需要更多时间来获取数据(因为它必须通过系统复制)。并且应该可以直接从 Java 访问它,而无需使用 JNI。在 Unix 下,还可以配置套接字以同样快速地响应(实际上,这就是命名管道的本质);我不知道 Java 接口是否支持这些配置选项,但是,同样,我不知道它们在 Windows 下是否可用。

    【讨论】:

      【解决方案3】:

      当您说延迟非常低时,您需要对其进行限定。您可以通过 Socket 环回发送消息,RTT 为 20 微秒。如果这足够快,我会这样做。

      如果这还不够快,我只需将 C++ 放在 Java 应用程序中并通过 JNI 调用它。这将为您提供大约 30 纳秒的 RTT。

      使用内存映射数据的问题在于获得正确的互锁。您可能会找到一种适用于一个系统但可能不适用于其他系统的解决方案。

      【讨论】:

      • +1 好点。我可以再补充一件事:如果处理消息(即产生答案)所需的时间是 5 毫秒,那么套接字的开销是 0.4%。我真的会看到这个应用程序的一些基准......
      【解决方案4】:

      我建议你使用 TCP 套接字

      根据我的经验,TCP 套接字的实际开销与应用程序的其他任务的工作量相比非常非常低,至少我用来开发的那些。我的意思是,有时即使套接字的延迟是其他 IPC 机制延迟的两倍,在整个工作流程中它们的影响也很小。它为您省去了在 Java 应用程序和 C++ 应用程序之间进行 IPC 的麻烦,这最终将要求您使用使用 JNI 的特定 Java 库,而 JNI 的开销和库本身的开销。

      实际上,在我的 Java 应用程序中,垃圾收集器的影响比“loopback”TCP 套接字造成的延迟重要得多。

      此外,TCP 套接字比传统 IPC 更具可扩展性(和便携性!)。如果将来您必须在不同的机器上运行客户端和服务器怎么办?在“TCP 套接字”场景中,您必须进行 5 分钟的 hack,在“传统 IPC”场景中,您必须重写整个 IPC 内容。

      但是,您的应用程序的一般工作流程是什么?

      即使不需要确认,我还是建议使用 TCP(而不是 UDP)来避免未排序的传递(这会导致在重新排列您收到的内容时感到头疼 - 您的一些消息是100KB,这不适合 UDP 数据包)。

      在回答你上一个问题时,为了让服务器通知客户端端口,你可以让服务器使用特定的“端口”命令行参数启动客户端,或者让服务器在 / 下保存一个小文件tmp(或另一个临时目录),里面写着端口号。

      【讨论】:

      • 谢谢,TCP 套接字听起来不错。我是否可以建立一个套接字(将两个程序之一指定为“服务器”)并在两个程序都在运行时保持连接打开,或者这会带来任何问题吗?
      • @Jen:您说您不希望用户需要“允许”使用端口。我很确定,即使您的两个 Java 进程想要在 OS X 上使用 TCP 套接字进行通信,并且默认的 OS X 10.5+ 安装将触发一个弹出窗口,询问该程序是否允许“打开“端口。即使它只是在两个本地进程之间。
      • @SyntaxT3rr0r:好点。我假设这个问题只被问过一次,然后程序被认为是“授权的”。如果没有,有没有办法避免用户每次都必须明确授权?
      • Windows 防火墙不会为环回侦听套接字弹出。我不知道在 OS X 上(默认情况下禁用防火墙,afaik)。要回答您的另一个问题,是的,您可以长时间保持 TCP 连接打开。小心服务器监听环回接口,而不是 INADDR_ANY。
      • "为了让服务器通知客户端端口,你可以让服务器启动带有特定'端口'命令行参数的客户端"好点
      【解决方案5】:

      另一种方法是使用内存映射文件并通过检查编译器设置来保持它的可移植性,如果你是 posix 或不是。 POSIX 操作系统有 mmap(),在 Windows 中你会使用 CreateFileMapping()

      boost 库中是 C++ 的可移植实现,在 java 中您应该可以使用 FileChannel()

      此页面很好地解释了如何将其用于 IPC http://en.wikipedia.org/wiki/Memory-mapped_file

      【讨论】:

      • +1 很好的提示,但在实现它时可能会非常复杂(随后进行准确的测试和调试),而性能增益几乎没有。此外,在将应用程序移植到适当的“分布式应用程序”时,您必须重写 IPC 部分,而使用套接字则不需要。 [可扩展性]
      • 是的,但我们使用它来进行 java 图像生成器和 C++ 控制器之间的通信。文件大小有点大,对我们来说,单向通信是有意义的。无需确认,制作两个内存映射文件应该不会太难,其中一个是 java2c,另一个是 c2java。
      • 正确。我们应该更多地了解询问者的申请。
      【解决方案6】:

      我听说过关于 ZeroMQ 的好消息,尤其适用于这类场景。在某些情况下,它甚至声称比 TCP 更快。总之,试一试肯定不会有什么坏处。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-12-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-12-14
        • 2019-06-03
        • 2017-06-24
        相关资源
        最近更新 更多