使用Jsch执行命令,并读取终端输出

jsch

http://www.jcraft.com/jsch/

Jsch是java实现的一个SSH客户端。开发JSCH的公司是 jcraft:

JCraft成立于1998年3月,是一家致力于Java应用程序和Internet / Intranet服务的应用程序开发公司。

Jcraft的总裁兼首席执行官是Atsuhiko Yamanaka博士

在Yamanaka博士于1998年3月创立JCraft之前,他已经加入NEC公司两年了,从事软件的研究和开发。

Yamanaka博士拥有日本东北大学的信息科学硕士学位和博士学位。他还获得了东北大学信息科学学士学位。他的主题与计算机科学的数学基础有关,尤其是构造逻辑和功能编程语言的设计。

执行命令

public static String executeCommandWithAuth(String command,                                              SubmitMachineInfo submitMachineInfo,                                             ExecuteCommandACallable<String> buffer) {     Session session = null;     Channel channel = null;     InputStream in = null;     InputStream er = null;     Watchdog watchdog = new Watchdog(120000);//2分钟超时     try {       String user = submitMachineInfo.getUser();       String host = submitMachineInfo.getHost();       int remotePort = submitMachineInfo.getPort();        JSch jsch = new JSch();       session = jsch.getSession(user, host, remotePort);        Properties prop = new Properties();       //File file = new File(SystemUtils.getUserHome() + "/.ssh/id_rsa");       //String knownHosts = SystemUtils.getUserHome() + "/.ssh/known_hosts".replace('/', File.separatorChar);       //jsch.setKnownHosts(knownHosts)       //jsch.addIdentity(file.getPath())       //prop.put("PreferredAuthentications", "publickey");       //prop.put("PreferredAuthentications", "password");       //        prop.put("StrictHostKeyChecking", "no");       session.setConfig(prop);       session.setPort(remotePort);       session.connect();        channel = session.openChannel("exec");       ((ChannelExec) channel).setPty(false);       ((ChannelExec) channel).setCommand(command);        // get I/O streams        in = channel.getInputStream();       er = ((ChannelExec) channel).getErrStream();       BufferedReader reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));       BufferedReader errorReader = new BufferedReader(new InputStreamReader(er, StandardCharsets.UTF_8));        Thread thread = Thread.currentThread();       watchdog.addTimeoutObserver(w -> thread.interrupt());        channel.connect();       watchdog.start();       String buf;       while ((buf = reader.readLine()) != null) {         buffer.appendBuffer(buf);         if (buffer.IamDone()) {           break;         }       }       String errbuf;       while ((errbuf = errorReader.readLine()) != null) {         buffer.appendBuffer(errbuf);         if (buffer.IamDone()) {           break;         }       }        //两分钟超时,无论什么代码,永久运行下去并不是我们期望的结果,       //加超时好处多多,至少能防止内存泄漏,也能符合我们的预期,程序结束,相关的命令也结束。       //如果程序是前台进程,不能break掉,那么可以使用nohup去启动,或者使用子shell,但外层我们的程序一定要能结束。       watchdog.stop();       channel.disconnect();       session.disconnect();     } catch (Exception e) {       e.printStackTrace();     } finally {       try {         if (in != null) {           in.close();         }         if (er != null) {           er.close();         }       } catch (Exception e) {         //       }        if (channel != null) {         channel.disconnect();       }       if (session != null) {         session.disconnect();       }       watchdog.stop();     }      return buffer.endBuffer(); }
  public interface ExecuteCommandACallable<T> {      boolean IamDone();//提前结束执行,如果终端是无限输出,则可以在达到一定条件的时候,通过IamDone通知上述程序结束读取。      //for buffer     ExecuteCommandACallable<T> appendBuffer(String content);//异步追加输出到自定义的Buffer      String endBuffer();//正常结束Buffer,   }

上述两段代码已经用于生产环境,如果通过异步的方式启动,可以在Buffer中通过appendBuffer方法接收每一行的输出。可以打印到终端,也可以写如文件,甚至写到websocket,Kafka等。

实际遇到的问题

就是执行一些命令,例如启动 spark,spark-submit,启动 flink, flink run,都无法读取终端输出,且都阻塞到readLine。

思路,既然我们读的是标准终端输出,以及错误终端输出,那么我们是见过