Py4j launch_gateway 连接不正确
Py4j launch_gateway not connecting properly
我正在尝试使用 py4j 打开一个网关,我可以使用该网关将对象从 java 传递到 python。当我尝试使用 py4j 函数 launch_gateway
it does not seem to properly connect to my Java class. However, when I launch my java class in the command line and then connect to it in python using JavaGateway
打开网关时,一切都按预期工作。我希望能够使用内置方法,因为我确信我没有考虑 py4j 设计中已经考虑过的事情,但我只是不确定我做错了什么。
假设我想创建一个通往 class sandbox.demo.solver.UtilityReporterEntryPoint.class
的网关。在命令行中,我可以通过执行以下命令来做到这一点:
java -cp /Users/grr/anaconda/share/py4j/py4j0.10.4.jar: sandbox.demo.solver.UtilityReporterEntryPoint py4j.GatewayServer
这按预期启动,我可以在连接到网关后从 python 中使用我的 class 中的方法。到目前为止一切顺利。
我对 py4j 文档的理解使我相信我应该执行以下操作以在 python 中启动网关:
port = launch_gateway(classpath='sandbox.demo.solver.UtilityReporterEntryPoint')
params = GatewayParameters(port=port)
gateway= JavaGateway(gateway_parameters=params)
执行这三行时我没有收到任何错误,但是当我尝试使用 gateway.entry_point.someMethod()
访问我的 java class 方法时,它失败并出现以下错误:
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
you can see that given the inputs you provide and those constructed by the function, a command is constructed that eventually gets called by subprocess.Popen
的 py4j 源代码中。因此,给定传递给 launch_gateway
以上的输入,传递给 Popen
的命令将是:
command = ['java', '-classpath', '/Users/grr/anaconda/share/py4j/py4j0.10.4.jar:sandbox.demo.solver.UtilityReporterEntryPoint', 'py4j.GatewayServer', '0']
按预期将此命令传递到 Popen
returns 侦听端口。但是,连接到这个监听端口仍然不允许访问我的 class 方法。
最后,将命令作为单个字符串传递给没有最后一个参数(“0”)的 Popen,正确启动网关,该网关再次按预期运行。浏览了 py4j.GatewayServer.class 的 Java 源代码后,这没有任何意义,因为主要方法似乎表明如果参数长度为 0,class 应该以状态 1 退出。
此时我有点不知所措。我可以找到一个可行的解决方案,但正如我所说,我确信这会忽略网关行为的重要方面,而且我不喜欢 hacky 解决方案。我很想在这一篇中标记@Barthelemy,但希望他能读到这篇文章。在此先感谢您的帮助。
编辑
目前我已经能够通过以下步骤解决这个问题。
将包括所有外部依赖项在内的整个项目打包到一个 jar 文件中 magABM-all.jar
,'Main-Class' 设置为 UtilityReporterEntryPoint
.
包含关于 --die-on-exit
的 if...else
块,就像它在 GatewayServer.java
中一样
使用subprocess.Popen
调用命令到运行项目jar。
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 进程终止或关闭,网关将关闭。
N.B 我绝不认为这是最终解决方案,因为 py4j 是由更聪明的人编写的,目的明确,我相信是一种在 py4j 的范围内管理这个确切工作流的方法。这只是权宜之计。
有几个问题:
launch_gateway
中的classpath参数应该是目录或jar文件,而不是class名称。例如,如果你想包含额外的 Java 库,你可以将它们添加到 classpath 参数。
调用 gateway.entry_point.someMethod()
时收到的错误意味着您没有入口点。当您调用 launch_gateway
时,JVM 以 GatewayServer.main 启动,它启动一个没有入口点的 GatewayServer:GatewayServer server = new GatewayServer(null, port)
。目前无法使用 launch_gateway
并指定入口点。
当您使用 java -cp /Users/grr/anaconda/share/py4j/py4j0.10.4.jar: sandbox.demo.solver.UtilityReporterEntryPoint py4j.GatewayServer
启动 JVM 时,我相信 JVM 使用 UtilityReporterEntryPoint 作为主要 class。尽管您没有提供代码,但我假设此 class 有一个 main 方法,并且它启动一个 GatewayServer 并以 UtilityReporterEntryPoint 实例作为入口点。请注意,冒号和 class 名称之间有一个空格,因此 UtilityReporterEntryPoint 被视为主要 class 而不是 class 路径的一部分。
我正在尝试使用 py4j 打开一个网关,我可以使用该网关将对象从 java 传递到 python。当我尝试使用 py4j 函数 launch_gateway
it does not seem to properly connect to my Java class. However, when I launch my java class in the command line and then connect to it in python using JavaGateway
打开网关时,一切都按预期工作。我希望能够使用内置方法,因为我确信我没有考虑 py4j 设计中已经考虑过的事情,但我只是不确定我做错了什么。
假设我想创建一个通往 class sandbox.demo.solver.UtilityReporterEntryPoint.class
的网关。在命令行中,我可以通过执行以下命令来做到这一点:
java -cp /Users/grr/anaconda/share/py4j/py4j0.10.4.jar: sandbox.demo.solver.UtilityReporterEntryPoint py4j.GatewayServer
这按预期启动,我可以在连接到网关后从 python 中使用我的 class 中的方法。到目前为止一切顺利。
我对 py4j 文档的理解使我相信我应该执行以下操作以在 python 中启动网关:
port = launch_gateway(classpath='sandbox.demo.solver.UtilityReporterEntryPoint')
params = GatewayParameters(port=port)
gateway= JavaGateway(gateway_parameters=params)
执行这三行时我没有收到任何错误,但是当我尝试使用 gateway.entry_point.someMethod()
访问我的 java class 方法时,它失败并出现以下错误:
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
you can see that given the inputs you provide and those constructed by the function, a command is constructed that eventually gets called by subprocess.Popen
的 py4j 源代码中。因此,给定传递给 launch_gateway
以上的输入,传递给 Popen
的命令将是:
command = ['java', '-classpath', '/Users/grr/anaconda/share/py4j/py4j0.10.4.jar:sandbox.demo.solver.UtilityReporterEntryPoint', 'py4j.GatewayServer', '0']
按预期将此命令传递到 Popen
returns 侦听端口。但是,连接到这个监听端口仍然不允许访问我的 class 方法。
最后,将命令作为单个字符串传递给没有最后一个参数(“0”)的 Popen,正确启动网关,该网关再次按预期运行。浏览了 py4j.GatewayServer.class 的 Java 源代码后,这没有任何意义,因为主要方法似乎表明如果参数长度为 0,class 应该以状态 1 退出。
此时我有点不知所措。我可以找到一个可行的解决方案,但正如我所说,我确信这会忽略网关行为的重要方面,而且我不喜欢 hacky 解决方案。我很想在这一篇中标记@Barthelemy,但希望他能读到这篇文章。在此先感谢您的帮助。
编辑
目前我已经能够通过以下步骤解决这个问题。
将包括所有外部依赖项在内的整个项目打包到一个 jar 文件中
magABM-all.jar
,'Main-Class' 设置为UtilityReporterEntryPoint
.包含关于
--die-on-exit
的if...else
块,就像它在GatewayServer.java
中一样
使用
subprocess.Popen
调用命令到运行项目jar。
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 进程终止或关闭,网关将关闭。
N.B 我绝不认为这是最终解决方案,因为 py4j 是由更聪明的人编写的,目的明确,我相信是一种在 py4j 的范围内管理这个确切工作流的方法。这只是权宜之计。
有几个问题:
launch_gateway
中的classpath参数应该是目录或jar文件,而不是class名称。例如,如果你想包含额外的 Java 库,你可以将它们添加到 classpath 参数。调用
gateway.entry_point.someMethod()
时收到的错误意味着您没有入口点。当您调用launch_gateway
时,JVM 以 GatewayServer.main 启动,它启动一个没有入口点的 GatewayServer:GatewayServer server = new GatewayServer(null, port)
。目前无法使用launch_gateway
并指定入口点。当您使用
java -cp /Users/grr/anaconda/share/py4j/py4j0.10.4.jar: sandbox.demo.solver.UtilityReporterEntryPoint py4j.GatewayServer
启动 JVM 时,我相信 JVM 使用 UtilityReporterEntryPoint 作为主要 class。尽管您没有提供代码,但我假设此 class 有一个 main 方法,并且它启动一个 GatewayServer 并以 UtilityReporterEntryPoint 实例作为入口点。请注意,冒号和 class 名称之间有一个空格,因此 UtilityReporterEntryPoint 被视为主要 class 而不是 class 路径的一部分。