【问题标题】:Communication between local JVMs本地 JVM 之间的通信
【发布时间】:2011-06-30 10:35:28
【问题描述】:

我的问题:我可以/应该采取什么方法在本地运行的两个或多个 JVM 实例之间进行通信?

问题的一些描述:
我正在为一个项目开发一个系统,该项目需要单独的 JVM 实例来完全隔离某些任务。

在它运行时,“父”JVM 将创建它期望执行的“子”JVM,然后将结果返回给它(以相对简单的 POJO 类的格式,或者可能是结构化的 XML 数据)。不应使用 SysErr/SysOut/SysIn 管道传输这些结果,因为子进程可能已经将它们用作其运行的一部分。

如果子 JVM 在一定时间内没有响应结果,父 JVM 应该能够向子 JVM 发出信号以停止处理,或终止子进程。否则,子 JVM 应该在完成其任务结束时正常退出。

迄今为止的研究:
我知道有许多技术可能有用,例如......

  • 使用 Java 的 RMI 库
  • 使用套接字传输对象
  • 使用分发库,例如 Cajo、Hessian

...但我很想听听其他人在选择其中一个或任何其他选项之前可能会考虑哪些方法。

感谢您对此的任何帮助或建议!

编辑:
要传输的数据量-相对较小,主要是少数 POJO,其中包含表示子结果的字符串执行。如果任何解决方案在处理大量信息时效率低下,这不太可能在我的系统中成为问题。转移的金额应该是相当静态的,所以这不是必须是可扩展的。

传输延迟-在这种情况下不是一个关键问题,尽管如果需要对结果进行任何“轮询”,这应该能够相当频繁,而不会产生大量开销,因此我可以保持响应稍后在此之上的 GUI(例如进度条)

【问题讨论】:

  • 也许您可以谈谈您需要传输多少数据以及延迟有多重要?除非您有明显的性能限制,否则您认为最简单的解决方案可能是最好的。
  • @Peter Lawrey 我已经为你添加了一些额外的细节,谢谢!

标签: java sockets jvm rmi cajo


【解决方案1】:

不是直接回答您的问题,而是对替代方案的建议。 你考虑过OSGI吗?

它允许您在同一个 jvm 中完全隔离地运行 java 项目。 它的美妙之处在于项目之间的通信非常容易通过服务进行(参见Core Specifications PDF 第 123 页)。这样就不会进行任何类型的“序列化”,因为数据和调用都在同一个 jvm 中。

此外,您对服务质量(响应时间等)的所有要求都消失了 - 您只需要担心在您想要使用服务时服务是 UP 还是 DOWN。为此,您有一个非常好的规范,称为声明式服务(参见 Enterprise Spec PDF 第 141 页)

很抱歉这个题外话,但我认为其他一些人可能会认为这是一个替代方案。

更新

为了回答您关于安全性的问题,我从未考虑过这种情况。我不相信有办法在 OSGI 中强制使用“内存”。

但是,有一种方法可以在 JVM 之外的不同 OSGI 运行时之间进行通信。它被称为远程服务(参见Enterprise Spec PDF,第 7 页)。他们还很好地讨论了在做类似事情时要考虑的因素(见 13.1 谬误)。

Apache Felix 的人(OSGI 的实现)我认为已经用 iPOJO 实现了这个,称为Distributed Services with iPOJO(他们的包装器使使用服务更容易)。我从来没有用过这个 - 如果我错了,请忽略我。

【讨论】:

  • 不需要道歉,这是一个有趣的建议。我面临的问题之一是子 JVM 执行的部分代码不受信任,因此必须受到限制(例如通过安全管理器)。作为其中的一部分,我需要限制内存使用以确保父 JVM 有足够的资源保持可用。但是,据我所知,我无法限制 JVM 中的内存使用以防止内存不足异常。 OSGI 是否对此提供任何保护/监管?
【解决方案2】:

我会将KryoNet 与本地套接字一起使用,因为它非常专注于序列化并且非常轻量级(您还可以获得远程方法调用!我现在正在使用它),但禁用套接字断开超时。

RMI 的工作原理基本上是你有一个远程类型并且远程类型实现了一个接口。这个接口是共享的。在您的本地计算机上,您通过 RMI 库将接口绑定到从 RMI 库“注入”内存中的代码,结果是您拥有满足接口但能够与远程对象通信的东西。

【讨论】:

  • 这看起来很有趣,确实可以满足我的需求。您是否发现它在某些方面完全缺乏,即使这些方面目前不会给您带来任何问题?
  • 一些问题与网络类的注册有关,以及与初始注册有关的竞争条件。但可以通过确保方法调用以正确的顺序完成来克服这些问题。另外,我发现它有时会在连接时使连接饱和(我在网络模拟器接口上运行),它给出的一些错误令人困惑,很难确定实际原因。
  • 另一件事:很难获得调用方法的连接,但我正在修改 kryonet 并添加该功能。如果我只是让客户谎报他们来自哪个连接,那么所有的破坏都可能会破裂。
  • 我只想说在这里启用 RMI 的源代码有多么小——它本质上是 600 行代码。几乎没有什么。但是效果真的很好。
  • 另一件事:它不支持一对多 RMI 调用(因此,如果您调用远程方法,它会在 100 个远程对象上调用相同的方法)。
【解决方案3】:

akkaother java actor frameworks 是另一个选项,它提供从actor model 派生的通信和其他好处。

【讨论】:

    【解决方案4】:

    如果您不能使用标准输入/标准输出,那么我会使用套接字。您需要在套接字之上添加某种序列化层(就像使用 stdin/stdout 一样),而 RMI 是一个非常易于使用且非常有效的此类层。

    如果您使用 RMI 并发现性能不够好,我会切换到一些更高效的序列化器 - 有 plenty of options

    我不会去任何靠近 Web 服务或 XML 的地方。与 RMI 相比,这似乎完全是浪费时间,可能需要更多的精力和更少的性能。

    【讨论】:

    • 谢谢你,我相信这是我所倾向的方向。由于传输的数据量相对较小,我相信我不应该面临任何真正的性能问题,但我会记录您的链接,以防这确实成为问题。
    【解决方案5】:

    似乎不再有多少人喜欢 RMI。

    选项:

    1. 网络服务。例如http://cxf.apache.org
    2. JMX。现在,这确实是一种在桌子底下使用 RMI 的方法,但它会起作用。
    3. 其他 IPC 协议;你引用了Hessian
    4. 使用套接字,甚至共享内存滚动你自己。 (在父项中打开一个映射文件,在子项中再次打开它。您仍然需要一些东西来进行同步。)

    值得注意的例子是 Apache ant(它为一个或另一个目的分叉各种 Jvm)、Apache maven 和 Tanukisoft 守护程序套件的开源变体。

    就我个人而言,我对 Web 服务非常熟悉,所以这就是我倾向于将事情变成钉子的锤子。典型的 JAX-WS+JAX-B 或 JAX-RS+JAX-B 服务是非常少的 CXF 代码,并为我管理所有数据序列化和反序列化。

    【讨论】:

    • 出于兴趣,您的偏好在哪里?我很感激当您只有我对上述项目的简短描述时,这可能有点难以说出口。
    【解决方案6】:

    上面提到过,但我想对 JMX 建议进行一些扩展。实际上,我们正在做的几乎与您计划做的一样(从我可以从您的各种 cmets 中收集到的信息)。我们出于各种原因开始使用 jmx,我将在这里提到其中的一些。一方面,jmx 是关于管理的,所以总的来说它非常适合您想要做的事情(特别是如果您已经计划为其他管理任务提供 jmx 服务)。您在 jmx 接口上所做的任何努力都将起到双重作用,因为您可以使用 jvisualvm 等 java 管理工具调用 api。这引出了我的下一点,这与您想要的最相关。 jdk 6 及更高版本中的新Attach API 非常甜蜜。它使您能够动态地发现正在运行的 jvm 并与之通信。例如,这允许您的“控制器”进程崩溃并重新启动并重新找到所有现有的工作进程。这是一个非常强大的系统的组成部分。上面提到了 jmx 基本上是 rmi 底层,但是,与直接使用 rmi 不同,您不需要管理所有连接细节(例如处理唯一端口、可发现性等)。 attach api 在 jdk 中有点像隐藏的宝石,因为它没有很好的文档记录。当我最初研究这些东西时,我不知道 api 的名称,所以弄清楚 jvisualvm 和 jconsole 中的“魔法”是如何工作的非常困难。最后,我看到了一篇类似this one 的文章,它展示了如何在您自己的程序中实际使用 attach api。

    【讨论】:

    • 附加 api 仅适用于子进程 id 。生成子进程后,您需要获取进程 ID。
    • @SureshSankar - 您不需要知道附加到进程的 pid,但它确实有帮助。
    • 如果你不知道子进程ID,你将如何控制或调用子进程中的方法。让我知道如何在没有进程 ID 的情况下连接到子进程。
    • @SureshSankar - 正如我在回答中所说,附加 api 允许您动态发现其他 java 进程。如果你有办法识别你的子进程,你可以在不知道它们的 pid 的情况下找到它们(这就是我们产品的工作原理)。
    【解决方案7】:

    虽然它是为 JVM 之间潜在的远程通信而设计的,但我想您会发现 Netty 在本地 JVM 实例之间也能很好地工作。

    它可能是同类 Java 中性能最高/最健壮/受广泛支持的库。

    【讨论】:

    • Netty 似乎非常受人尊敬,但在我看来,它的专长在于稍微不同的问题。你有没有尝试过以这种方式使用 Netty?此外,它似乎对我的需求来说可能有点重 - 根据您的经验,学习/使用是否需要大量投资?
    • 它确实需要一些设置——尽管一旦你开始工作,它就会坚如磐石。如果您真的在寻找更轻量级的东西,我可能会推荐 KryoNet
    【解决方案8】:

    上面讨论了很多。但无论是套接字、rmi、jms - 都涉及大量肮脏的工作。 我会提出建议akka。它是一个基于 Actor 的模型,使用消息相互通信。

    美妙之处在于,演员可以在同一个 JVM 或另一个(非常少的配置)上,而 akka 会为您处理剩下的事情。我还没有看到比这样做更清洁的方法:)

    【讨论】:

      【解决方案9】:

      如果要传送的数据不是很大,试试jGroups

      【讨论】:

      • 我已为此添加了书签,以便更全面地查看它,谢谢。您有使用此功能的经验吗?如果有,您是否遇到过真正的困难?
      • 不多,我也在为我正在做的一个项目评估这个。暂时没有太多经验。您可以在他们的邮件列表中发送邮件。此外,它还被 jBoss 用于集群成员资格。
      • 如果您的应用程序不需要健壮并且不会看到显着的负载,那么它可能是合适的。我在两个工作中使用了 JGroups——在这两种情况下都是现状,在两种情况下我们都迁移到了其他解决方案。我发现它既消耗线程又消耗内存,其无限的可配置性使其无法合理配置。
      • @Ron 你能详细说明你的答案吗?另外,你可能会回答这个问题的一部分:stackoverflow.com/questions/4980677/…(反正我还没有任何像样的答案)
      • 如果:(1) gc 停顿是不常见的并且非常短; (2) 你不介意无限可配置、无限易理解的 jgroups 配置; (3) 你不介意线程和缓冲区和缓存的混乱 jgroups 运行时在你的 jvm 中旋转,那么 jgroups 是一个不错的选择。
      【解决方案10】:

      http://code.google.com/p/protobuf/ 怎么样? 它是轻量级的。

      【讨论】:

      • 我尝试使用带有套接字的 Protobuf,很难正常工作,因为您需要能够在接收后将输出重定向到正确的类,但要做到这一点,您需要从缓冲区中读取字段.您可以使用 CodedInputStream,但这并不好。如果可以的话,最好使用 bean 并将它们序列化。
      【解决方案11】:

      正如您所提到的,您显然可以通过网络发送对象,但这是一件昂贵的事情,更不用说启动一个单独的 JVM。

      如果您只想在一个 JVM 中分离不同的世界,另一种方法是使用不同的类加载器加载类。 ClassA@CL1!=ClassA@CL2 如果它们被 CL1 和 CL2 作为同级类加载器加载。

      要启用 classA@CL1 和 classA@CL2 之间的通信,您可以使用三个类加载器。

      • 加载进程1的CL1
      • 加载 process2 的 CL2(与 CL1 中的类相同)
      • 加载通信类(POJO 和服务)的 CL3。

      现在让 CL3 成为 CL1 和 CL2 的父类加载器。

      在 CL3 加载的类中,您可以在 CL1 中的类和 CL2 中的类之间拥有轻量级的通信发送/接收功能(发送(Pojo)/接收(Pojo))POJO。

      在 CL3 中,您公开了一个静态服务,该服务使 CL1 和 CL2 寄存器中的实现能够发送和接收 POJO。

      【讨论】:

      • 他问的是 JVM,而不是类加载器。
      • 感谢您的建议,但正如 @bmargulies 指出的那样,由于一些问题,我确实需要单独的 JVM
      猜你喜欢
      • 1970-01-01
      • 2010-10-23
      • 2017-09-19
      • 2020-05-19
      • 1970-01-01
      • 2019-03-26
      • 1970-01-01
      • 2020-12-04
      • 1970-01-01
      相关资源
      最近更新 更多