2017-04-07 41 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