簡體   English   中英

使用Bash ssh在多個遠程計算機上運行Java程序

[英]Use Bash ssh to run a Java program on multiple remote machines

我需要在許多遠程計算機上運行Java程序。 我在循環中使用ssh並調用運行Java程序的遠程腳本。

可以想象,這用於測試集群中的分布式系統。

問題是,在我輸入第一個ssh會話的密碼后,腳本立即掛起。 這可能是bash錯誤,因為Java程序在本地運行良好。

確切的結構是這樣,一個運行許多遠程bash腳本的本地bash腳本。 每個遠程腳本都會編譯並運行一個Java程序。 這個Java程序啟動一個單獨的線程來執行一些工作。 收到SIGINT信號后,將通知Java線程,使其可以干凈地退出。

我做了一個簡化的工作示例。

編輯:下面的代碼現在可以工作(后代固定)

請,如果您想回答,請不要過多更改代碼的結構,否則它將與原始代碼不相似,並且我將無法理解問題所在。

手動運行的Bash腳本

#!/bin/bash

function startBatch()
{
    #the problem was using -n
    ssh -f "$1" "cd $projectDir;./startBatch.sh $2"
}

function stopBatch()
{
    #the problem was using -n
    ssh -f "$1" "pkill -f jnode_.*"
}

projectDir=NetBeansProjects/Runner

#start nodes
nodeNumber=0
while read node; do
    startBatch "$node" "$nodeNumber"
    nodeNumber=$(($nodeNumber + 1))
done < ./nodes.txt

sleep 3

#stop nodes
while read node; do
    stopBatch "$node"
done < ./nodes.txt

由其他腳本運行的Bash腳本

#!/bin/bash

#this is a simplified working example
myNumber=$1
$(exec -a jnode_"$myNumber" java -cp build/classes runner.Runner "$myNumber.txt")

這是上面腳本的簡化版本。 如果要正確記錄,請檢查已接受答案的第二部分。

#!/bin/bash

batchNumber=$1
procNumber=0
batchSize=3
while [ "$procNumber" -lt "$batchSize" ]; do
    procName="$batchNumber"_"$procNumber"
    #this line was no good
    #$(exec -a jnode_"$procName" java -cp build/classes runner.Runner "$procName.txt" &)
    #this line works fine
    exec -a jnode_"$procName" java -cp build/classes runner.Runner "$procName.txt" 1>/dev/null 2>/dev/null &
    procNumber=$(($procNumber + 1))
done

Java Runner(啟動線程的事物)

import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintStream;

public class Runner {

    public static void main(String[] args) throws FileNotFoundException, InterruptedException {
        //redirect all outputs to a given file
        PrintStream output = new PrintStream(new File(args[0]));
        System.setOut(output);
        System.setErr(output);

        //controlled object
        final MyRunnable myRunnable = new MyRunnable();

        //shutdown the controlled process on command
        Runtime.getRuntime().addShutdownHook(new Thread() {
            @Override
            public void run() {
                myRunnable.stop = true;
            }
        });

        //run the process
        new Thread(myRunnable).start();
    }
}

Java MyRunnable(正在運行的線程)

public class MyRunnable implements Runnable {

    public boolean stop = false;

    @Override
    public void run() {
        while (!stop) {
            try {
                System.out.println("running");
                Thread.sleep(1000);
            } catch (InterruptedException ex) {
                System.out.println("interrupted");
            }
        }
        System.out.println("stopping");
    }
}

不要在Java程序中使用System.exit(),否則將無法正確調用(或完全執行)關閉掛鈎。 從外部發送SIGINT消息。


正如在注釋中提到的那樣,輸入密碼可能很無聊。 可以選擇無密碼的RSA密鑰,但我們可以做得更好。 讓我們添加一些安全功能。

創建公鑰/私鑰對

ssh-keygen -t rsa
Enter file in which to save the key (home/your_user/.ssh/id_rsa): [input ~/.ssh/nameOfKey]
Enter passphrase (empty for no passphrase): [input a passphrase not weaker than your ssh password]

將公鑰添加到遠程主機的authorized_keys文件中,以便可以對其進行身份驗證。

#first option (use proper command)
ssh-copy-id user@123.45.67.89

#second option (append the key at the end of the file)
cat ~/.ssh/nameOfKey.pub | ssh user@123.45.67.89 "cat >> ~/.ssh/authorized_keys"

現在,如果我們使用ssh-agent,我們可以做到這一點,這樣密碼短語將僅被詢問一次(執行第一個命令時)。 注意,它將要求輸入密碼(創建密鑰時輸入的密碼),而不是實際的ssh密碼。

#activate the agent
eval `ssh-agent`

#add the key, its passphrase will be asked
ssh-add ~/.ssh/keyName1

#add more keys, if needed
ssh-add ~/.ssh/keyName2

現在,您已經為分布式系統提供了一個非常簡單但功能正常的測試框架。 玩得開心。

ssh的手冊頁建議,如果ssh需要輸入密碼,則無法使用-n 您應該使用-f ,或者設置無密碼的ssh,因此您無需輸入密碼。

從Mac OS X手冊頁引用ssh可得到:

 -n      Redirects stdin from /dev/null (actually, prevents reading from stdin).  This must be used when ssh is run in the background.  A common trick is to use this to run X11
         programs on a remote machine.  For example, ssh -n shadows.cs.hut.fi emacs & will start an emacs on shadows.cs.hut.fi, and the X11 connection will be automatically for-
         warded over an encrypted channel.  The ssh program will be put in the background.  (This does not work if ssh needs to ask for a password or passphrase; see also the -f
         option.)

並且:

 -f      Requests ssh to go to background just before command execution.  This is useful if ssh is going to ask for passwords or passphrases, but the user wants it in the back-
         ground.  This implies -n.  The recommended way to start X11 programs at a remote site is with something like ssh -f host xterm.

         If the ExitOnForwardFailure configuration option is set to ``yes'', then a client started with -f will wait for all remote port forwards to be successfully established
         before placing itself in the background.

執行遠程命令時,直到遠程命令完成,SSH才會退出。 在Java程序完成之前,您的遠程腳本不會退出,在所有非守護程序線程都退出之前,Java程序也不會退出,並且Java程序將永遠運行。 因此,服務器端對SSH的調用將永遠運行(好吧,直到通過其他方法將其殺死),腳本就會掛起。

您需要確定一種使SSH遠程命令立即返回的方法。 您有選擇。 最簡單的方法可能是在服務器腳本上使用&調用它,如下所示:

ssh -n "$1" "cd $projectDir;./startBatch.sh $2 &"

一個更健壯的選項是在遠程腳本中使用&調用java ,並讓服務器端立即運行(否& ),這樣您就有機會完全讀取例如由遠程腳本產生的錯誤消息。

旁注:至於密碼本身(一旦超過當前障礙,您最終將不得不處理該密碼),如我對以下問題的評論中所述:一種可能性是創建無密碼密鑰( ssh-keygen -t rsa ),然后將公共密鑰放在每台遠程計算機上的authorized_keys2 ,那么從計算機進行連接時,您無需處理密碼。 SSH密碼提示有時會嚴重破壞腳本交互性。 附帶有相關的安全隱患,但它們可能對您的情況無關緊要。


回應以下評論。 您有兩種選擇。 如果您想使用append將所有內容捕獲到相同的日志文件中,請不要重定向程序輸出,而只將while循環將所有內容重定向到日志,例如:

while [ "$procNumber" -lt "$batchSize" ]; do
    procName="$batchNumber"_"$procNumber"
    exec -a jnode_"$procName" java -cp build/classes runner.Runner "$procName.txt" &
    procNumber=$(($procNumber + 1))
done >> "$myLog" 2>&1

如果您希望每個進程一個日志,請附加:

while [ "$procNumber" -lt "$batchSize" ]; do
    procName="$batchNumber"_"$procNumber"
    exec -a jnode_"$procName" java -cp build/classes runner.Runner "$procName.txt" >> "$myLog.$procNumber" 2>&1 &
    procNumber=$(($procNumber + 1))
done

如果要在循環中將應用程序輸出與其他命令的輸出分開,也可以將以上兩者結合起來。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM