Java如何在Linux服务器上调用shell、py等可执行文件
通常你会这么做:
1 |
|
实时上这样写不会生效
正确的姿势
1 |
|
如何添加超时机制
可以看到上面的写法会阻塞等待,若指令是一个读取大文件、或者一个长时间业务脚本,则会导致业务系统阻塞,这时就需要考虑超时机制了。
如果是使用JDK1.8+ 可以这样做
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
String[] commands = {"/bin/sh", "-c", cmd};
Process process = null;
try {
process = Runtime.getRuntime().exec(commands);
if (!process.waitFor(timeout, TimeUnit.MILLISECONDS)) {
process.destroyForcibly();
process = null;
log.info("time out");
}
try (InputStream is = process.getInputStream();
BufferedReader stdInput = new BufferedReader(new InputStreamReader(is));
BufferedReader stdError = new BufferedReader(new InputStreamReader(is))) {
char[] data = new char[bufferSize];
stdInput.read(data, 0, data.length);
stdInputInfo.append(data, 0, data.length);
stdError.read(data, 0, data.length);
stdErrorInfo.append(data, 0, data.length);
} catch (Exception ignore) {
log.error(ignore.getMessage(), ignore);
}
} catch (IOException e) {
log.error("执行命令失败:{}", e);
} finally {
if (process != null) {
process.destroy();
}
}如果是低版本JDK则考虑使用线程Join的方式来包裹指令来实现
JDK1.8中Linux的waitFor实现方式
1 | public boolean waitFor(long timeout, TimeUnit unit) |
- 进程关闭不完全的问题如何处理,目前遇到在脚本中通过管道、Python脚本等方式会存在父子进程,超时destroy只会关闭父进程,而子进程还是在继续执,这样并没有达到超时终止任务的目的。目前通过下面的方式处理的
1
2
3
4
5
6
7
8
9
10
11
12
13if (killAll) {
Field f = process.getClass().getDeclaredField("pid");
f.setAccessible(true);
long pid = f.getInt(process);//获取父进程pid
//通过pkill来关闭所有进程。防止子进程未结束
Process kill = Runtime.getRuntime().exec(new String[]{"/bin/sh", "-c", "pkill -P " + pid});
kill.waitFor();
} else {
process = process.destroyForcibly();//只会销毁当前进程
process.waitFor();
}
process = null;