实际应用中,有时候需要从web管理界面上,远程去启动其它linux主机上的程序,利用ssh协议可以方便的满足这一需求。事实上hadoop架构中,从nn上启动dn时,就是利用了免密码ssh登录。ganymed-ssh2是一个实现了ssh协议的开源项目,项目地址为:http://ganymed-ssh-2.googlecode.com/ (下载源码要翻强,众所周知的原因),如果只是使用的话,pom.xml添加以下依赖项就行了:
1 <dependency> 2 <groupId>ch.ethz.ganymed</groupId> 3 <artifactId>ganymed-ssh2</artifactId> 4 <version>262</version> 5 </dependency>
为了方便起见,封装了一个工具类SSHUtil.java(已托管在taobao.org上)
package com.cnblogs.yjmyzz.utils; import ch.ethz.ssh2.Connection; import ch.ethz.ssh2.Session; import ch.ethz.ssh2.StreamGobbler; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; /** * SSH工具类(可远程执行其它Linux机器上的Shell命令) * Created by jimmy on 2015/7/6. * http://code.taobao.org/p/y-lib/src/trunk/src/main/java/com/cnblogs/yjmyzz/utils/SSHUtil.java */ public class SSHUtil { /** * 连接到主机 * * @param hostname * @param username * @param password * @return * @throws Exception */ private static Connection getConnection(String hostname, String username, String password) throws Exception { Connection conn = null; try { conn = new Connection(hostname); conn.connect(); boolean isAuthenticated = conn.authenticateWithPassword(username, password); if (isAuthenticated == false) { throw new IOException("Authentication failed."); } } catch (Exception e) { throw new IOException("username or password error."); } return conn; } /** * 执行远程命令 * * @param hostname 远程主机IP * @param username 用户名 * @param password 密码 * @param command 需要执行的命令 * @param timeout 超时时间(秒) * @return * @throws Exception */ public static String execRemoteCommand(String hostname, String username, String password, String command, long timeout) throws Exception { Connection conn = getConnection(hostname, username, password); StringBuilder sb = new StringBuilder(); Session session = null; try { session = conn.openSession(); session.requestPTY("vt100", 80, 24, 640, 480, null); session.execCommand(command); InputStream stdout = new StreamGobbler(session.getStdout()); BufferedReader br = new BufferedReader(new InputStreamReader(stdout)); long start = System.currentTimeMillis(); char[] arr = new char[512]; int read; int i = 0; while (true) { read = br.read(arr, 0, arr.length); if (read < 0 || (System.currentTimeMillis() - start) > timeout * 1000) { break; } sb.append(new String(arr, 0, read)); i++; } } finally { if (session != null) { session.close(); } if (conn != null) { conn.close(); } } return sb.toString(); } /** * 执行远程命令(默认5秒超时) * * @param hostname 远程主机IP * @param username 用户名 * @param password 密码 * @param command 需要执行的命令 * @return * @throws Exception */ public static String execRemoteCommand(String hostname, String username, String password, String command) throws Exception { return execRemoteCommand(hostname, username, password, command, 5); } /** * 批量执行远程命令 * * @param hostname 远程主机IP * @param username 用户名 * @param password 密码 * @param command 需要执行的命令列表 * @param timeout 超时时间(秒) * @return * @throws Exception */ public static String execRemoteCommand(String hostname, String username, String password, String[] command, long timeout) throws Exception { Connection conn = getConnection(hostname, username, password); StringBuilder sb = new StringBuilder(); Session session = null; try { for (int t = 0; t < command.length; t++) { session = conn.openSession(); session.requestPTY("vt100", 80, 24, 640, 480, null); session.execCommand(command[t]); InputStream stdout = new StreamGobbler(session.getStdout()); BufferedReader br = new BufferedReader(new InputStreamReader(stdout)); long start = System.currentTimeMillis(); char[] arr = new char[512]; int read; int i = 0; while (true) { read = br.read(arr, 0, arr.length); if (read < 0 || (System.currentTimeMillis() - start) > timeout * 1000) { break; } sb.append(new String(arr, 0, read)); i++; } session.close(); } } finally { if (conn != null) { conn.close(); } } return sb.toString(); } /** * 批量执行远程命令(默认5秒超时) * * @param hostname 远程主机IP * @param username 用户名 * @param password 密码 * @param command 需要执行的命令列表 * @return * @throws Exception */ public static String execRemoteCommand(String hostname, String username, String password, String[] command) throws Exception { return execRemoteCommand(hostname, username, password, command, 5); } }
使用要点:
1. 如果要连续执行多个命令,用&&连接,比如:先 cd / 切换到根目录,然后再ls 根目录下的所有文件,可以这样调用:
public static void main(String[] args) { String hostname = "172.21.129.**"; String username = "root"; String password = "***"; try { System.out.println(SSHUtil.execRemoteCommand(hostname, username, password, "pwd&&cd /&&pwd&&ls")); } catch (Exception e) { e.printStackTrace(); } }
上面的命令相当于在同一个session下,连续执行
pwd
cd /
pwd
ls
2. 如果要以后台进程调用命令,传入命令时,直接加 nohup 即可