2017-04-07 83 views
0

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

比方说,我想创建一个门户类sandbox.demo.solver.UtilityReporterEntryPoint.class。在命令行中,我可以通过执行以下操作如下:

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

如预期,我可以连接到网关后,蟒蛇内使用的方法在我的课从这个启动。到现在为止还挺好。

我的py4j文件的理解将导致我相信我应该做到以下几点启动网关蟒蛇:

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

我执行这三条线时没有错误,但是当我尝试访问我与gateway.entry_point.someMethod() Java类方法失败,出现以下错误:

Py4JError: An error occurred while calling t.getReport. Trace: py4j.Py4JException: Target Object ID does not exist for this gateway :t at py4j.Gateway.invoke(Gateway.java:277) at py4j.commands.AbstractCommand.invokeMethod(AbstractCommand.java:132) at py4j.commands.CallCommand.execute(CallCommand.java:79) at py4j.GatewayConnection.run(GatewayConnection.java:214) at 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退出。

在这一点上,我有点不知所措。我可以用自己的方式进入一个可行的解决方案,但正如我所说的,我肯定忽略了网关行为的重要方面,我不喜欢哈克解决方案。我很想在这个标签中添加@Barthelemy,但希望他读到这个。预先感谢您的帮助。

编辑

现在我已经能够解决这个问题,下面的步骤。

  1. 包装整个项目在内的所有外部依赖到一个JAR文件magABM-all.jar,与“主类”设置为UtilityReporterEntryPoint

  2. 包括if...else关于--die-on-exit存在块酷似它在GatewayServer.java

  3. 使用subprocess.Popen调用命令运行项目罐子。

启动该py4j网关

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,如果蟒蛇进程死亡或者是关闭的网关将被关闭。

NB我绝不认为这是一个最终的解决方案py4j被写了更聪明的人在考虑一个明确的目的,我相信,有管理py4j的范围内这一确切工作流的方式。这只是一个权宜之计。

回答

1

有几个问题:

  1. launch_gateway类路径参数应是一个目录或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作为主类。虽然您没有提供代码,但我认为此类具有主要方法,并且它以UtilityReporterEntryPoint实例作为入口点启动GatewayServer。请注意冒号和类名之间有一个空格,所以UtilityReporterEntryPoint被视为主类,而不是作为类路径的一部分。

+0

感谢您的回复。我想我对第2点有点困惑。你是否用'launch_gateway'说我没有办法启动我的类'UtilityReporterEntryPoint'?如果有,我是否有办法在启动后设置入口点? – Grr

+0

我仍然想更多地了解如何正确使用'launch_gateway'方法,但现在我想出了一些解决方法。看看我更新的问题,并让我知道这是否合理。就像我说过的,我真的很想知道如何以正确的方式做到这一点,而不仅仅是一些冒险的修复。 – Grr

+0

@Grr无法使用launch_gateway指定主类,因此无法指定入口点。 launch_gateway的目标是尽快开始。只要你需要更多的配置选项,最好推出自己的策略,比如你创建的app.setup_gateway函数。您也可以考虑打开一个功能请求来支持自定义Main类,但正如您在--die-on-exit时看到的那样,Main类仍然需要遵循某些协议。 – Barthelemy