目前市面上执行远程操作可选方案并不多,一般用到两种:jsch 和 ganymed ssh-2
第一种:使用ganymed ssh-2
优点:是基于原生java的ssh操作开源库,特点是api语义清晰使用方便,license友好
缺点:2014年就已经停止更新维护了
mvn依赖配置:
<dependency>
<groupId>ch.ethz.ganymed</groupId>
<artifactId>ganymed-ssh2</artifactId>
<version>262</version>
</dependency>
连接方式支持账号密码登陆和秘钥登陆
Connection conn = new Connection("ip地址");
conn.connect(null, Constant.CONNECT_OUT_TIME, Constant.KEX_OUT_TIME);
//账号密码登陆
conn.authenticateWithPassword(userName, password);
//秘钥登陆
conn.authenticateWithPublicKey(userName, new File("秘钥绝对路径"), password);
执行远程命令
session1 = connection.openSession();
//执行rsync同步
String cmd = String.format("rsync --protect-args -avlPz "%s" "%s@%s:%s" ", sourcePath,
targetUsername, targetIp, targetParentPath.replaceAll("\s", "\ "));
//输出命令执行日志
String stdouts = " >>/var/log/vss/" + executeLog.getId() + ".log";
String command = String.format("sshpass -p %s ", targetPassword) + cmd + stdouts;
session.execCommand(command);
// pid 可以新会话session2 查询第一个会话命令在linux的执行进程id 保存到
session2 = connection.openSession();
String vssFlag = targetPath.substring(targetPath.lastIndexOf("/"));
session2.execCommand("ps -ef|grep rsync|grep " + vssFlag);
br = new BufferedReader(new InputStreamReader(pssession.getStdout()));
String line;
while ((line = br.readLine()) != null) {
if (line.endsWith(stdouts)) {
String[] temsp = line.split("\s+");
cmd_id = Integer.parseInt(temsp[1]);
//cmd_id 执行命令进程号 可以保存到数据库;当执行退出后,可以再次连接到服务器,
然后查询进程是否真正退出结束了
}
}
// pid end 等待远程执行命令的退出码 超时时间为0 表示无限期等待下去 当命令执行时间过长时
//存在退出码已经返回,执行命令的进程还未退出的情况
int condition = session.waitForCondition(ChannelCondition.EXIT_STATUS, 0);
if ((condition & ChannelCondition.EXIT_STATUS) != 0) {
Integer exitCode = session.getExitStatus();
log.info("EXIT_STATUS {} ", exitCode);
if (exitCode != null && exitCode != 0) {
errorReader = new BufferedReader(new InputStreamReader(session.getStderr()));
StringBuilder sb = new StringBuilder();
line = null;
while ((line = errorReader.readLine()) != null) {
log.error(line);
sb.append(line);
}
throw new IOException(sb.toString());
}
}
常见问题
-
Session对象只能执行一次execCommand操作,调用多次会报错。如果想要调用多条命令,彼此不依赖可以创建多个Session对象,也可以用";"分割多条操作一次传入;
-
session的waitForCondition(int channelCondition, long timeout)方法可以等待期待的状态来临。遇到过流读完了但ExitCode还是Null的情况,所以保险期间需要等待获得远程进程的退出状态。ChannelCondition里的常量定义了相关的状态码,同一时间状态可以有多个,输入用或操作|连接多个状态码, 可以表达多个状态任意一个达成即可的语义。除了指定的状态来临,当超时或session关闭时,方法也会立刻返回。返回值也是int channelCondition,可以跟ChannelCondition的常量与操作 &,判断是否有期待的状态。