Py4j launch_gateway没有正确连接

时间:2017-04-07 15:56:01

标签: java python gateway py4j

我正在尝试使用py4j来打开一个可以用来将对象从java传递到python的网关。当我尝试使用py4j函数launch_gateway打开网关时,它似乎没有正确连接到我的Java类。但是,当我在命令行中启动我的java类,然后使用JavaGateway在python中连接它时,一切都按预期工作。我希望能够使用内置方法,因为我确信我不会考虑py4j设计中已经考虑过的事情,但我只是不确定我做错了什么。

假设我想创建一个类sandbox.demo.solver.UtilityReporterEntryPoint.class的网关。在命令行中,我可以通过执行以下命令来执行此操作:

java -cp /Users/grr/anaconda/share/py4j/py4j0.10.4.jar: sandbox.demo.solver.UtilityReporterEntryPoint py4j.GatewayServer

这会按预期启动,我可以在连接到网关后在python中使用我的类中的方法。到目前为止一切都很好。

我对py4j文档的理解会让我相信我应该执行以下操作来在python中启动网关:

port = launch_gateway(classpath='sandbox.demo.solver.UtilityReporterEntryPoint')
params = GatewayParameters(port=port)
gateway= JavaGateway(gateway_parameters=params)

执行这三行时没有错误,但是当我尝试使用gateway.entry_point.someMethod()访问我的java类方法时,它失败并出现以下错误:

  

Py4JError:调用t.getReport时发生错误。跟踪:   py4j.Py4JException:此网关不存在目标对象ID:t       在py4j.Gateway.invoke(Gateway.java:277)       at py4j.commands.AbstractCommand.invokeMethod(AbstractCommand.java:132)       在py4j.commands.CallCommand.execute(CallCommand.java:79)       在py4j.GatewayConnection.run(GatewayConnection.java:214)       在java.lang.Thread.run(Thread.java:745)

显然在launch_gateway内没有正确调用某些内容,或者我正在向它提供错误的信息。

launch_gateway的py4j源代码中,您可以看到,根据您提供的输入和函数构造的输入,构造的命令最终由subprocess.Popen调用。因此,传递给launch_gateway的输入传递给Popen的命令将是:

command = ['java', '-classpath', '/Users/grr/anaconda/share/py4j/py4j0.10.4.jar:sandbox.demo.solver.UtilityReporterEntryPoint', 'py4j.GatewayServer', '0']

将此命令传递给Popen会按预期返回侦听端口。但是,连接到此侦听端口仍然不允许访问我的类方法。

最后,将命令作为单个字符串传递给Popen而没有最后一个参数('0'),正确启动一个网关,它再次按预期运行。看一下py4j.GatewayServer.class的Java源代码,这没有任何意义,因为主方法似乎表明如果参数的长度为0,该类应该以状态1退出。

此时我有些不知所措。我可以破解我的方式进入一个可行的解决方案,但正如我所说,我确信忽略了网关行为的重要方面,我不喜欢hacky解决方案。我很乐意在这篇文章中标记@Barthelemy,但希望他能读到这一点。提前感谢您的帮助。

编辑

目前我已经能够通过以下步骤解决此问题。

  1. 将整个项目(包括所有外部依赖项)打包到一个jar文件magABM-all.jar中,并将“Main-Class”设置为UtilityReporterEntryPoint

  2. GatewayServer.java

  3. 中加入与if...else完全相同的--die-on-exit
  4. 使用subprocess.Popen调用命令来运行项目jar。

  5. UtilityReporterEntryPoint.java

    public static void main(String[] args) throws IOException {
      GatewayServer server = new GatewayServer(new UtilityReporterEntryPoint());
      System.out.println("Gateway Server Started");
      server.start();
      if (args[0].equals("--die-on-exit")) {
        try {
            BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in, Charset.forName("UTF-8")));
            stdin.readLine();
            System.exit(0);
        } catch (java.io.IOException e) {
            System.exit(1);
        }
      }
    }
    

    app.py

    def setup_gateway()
        """Launch a py4j gateway using UtilityReporterEntryPoint."""
        process = subprocess.Popen('java -jar magABM-all.jar --die-on-exit', shell=True)
        time.sleep(0.5)
        gateway = JavaGateway()
        return gateway
    

    这样我仍然可以在必要时使用gateway.shutdown,如果启动py4j网关的python进程死亡或关闭,网关将被关闭。

    NB 我绝不会认为这是最终解决方案,因为py4j是由更聪明的人编写的,其目的明确,我确信有一种方法可以管理这个确切的工作流程py4j的范围。这只是一个权宜之计。

1 个答案:

答案 0 :(得分:1)

有一些问题:

  1. launch_gateway中的classpath参数应该是目录或jar文件,而不是类名。例如,如果要包含其他Java库,则可以将它们添加到classpath参数中。

  2. 拨打gateway.entry_point.someMethod()时收到的错误表示您没有入口点。当您调用launch_gateway时,JVM以GatewayServer.main启动,它启动没有入口点的GatewayServer:GatewayServer server = new GatewayServer(null, port)。目前无法使用launch_gateway并指定入口点。

  3. 使用java -cp /Users/grr/anaconda/share/py4j/py4j0.10.4.jar: sandbox.demo.solver.UtilityReporterEntryPoint py4j.GatewayServer启动JVM时,我相信JVM使用UtilityReporterEntryPoint作为主类。虽然您没有提供代码,但我假设此类有一个main方法,并且它启动一个GatewayReporterEntryPoint实例作为入口点的GatewayServer。请注意冒号和类名之间有一个空格,因此UtilityReporterEntryPoint被视为主类,而不是类路径的一部分。