问题背景
之前碰到过一个奇葩的问题,在这里记录一下。
首先我们的项目是微服务架构,很多功能都是需要执行一段shell命令来进行操作服务器,
有一次碰到功能执行一直处于运行中状态,查看日志也是只能看到去执行了shell命令。然后我们手动吧执行的shell命令拿出来在服务器上执行,发现是正常运行。这个问题困扰了好久。。。。
问题原因
后续经过我的百度之旅,想到了一个原因,当然最后也确实也是这个原因。那就是我们在程序中通过Process运行shell命令导致的
Process process = Runtime.getRuntime().exec(shPath);
int exitCode = process .waitFor();
Runtime.getRuntime()返回当前应用程序的Runtime对象,该对象的exec()方法指示Java虚拟机创建一个子进程执行指定的可执行程序,并返回与该子进程对应的Process对象实例。通过Process可以控制该子进程的执行或获取该子进程的信息。
它的操作通过三个流(getOutputStream(),getInputStream(),getErrorStream())重定向到父进程。父进程使用这些流来提供到子进程的输入和获得从子进程的输出。因为输入和输出流提供有限的缓冲区大小,如果读写子进程的输出流或输入流出现失败,当缓冲区满之后将无法继续写入数据,则可能导致子进程阻塞,最终造成阻塞在waifor()这里。
问题解决
知道这个问题之后解决起来就容易了,就是要清空getInputStream()
和getErrorStream()
这两个流。而且两个流的清空一定是异步的。我在我们系统中是通过下面这种方式解决的
//线程池
private TaskThreadPoolConfig taskThreadPool = SpringContextUtil.getBean(TaskThreadPoolConfig.class);
/**
* 执行脚本方法
*
* @param sh
*/
public Integer execShell(final String sh) throws Exception {
final Process process = Runtime.getRuntime().exec(sh);
// 方法阻塞,等待命令执行完成
final int exitVal = process.waitFor();
taskThreadPool.taskThreadPool().submit(new Runnable() {
@Override
public void run() {
// 读取输出
String line = null;
try {
// 获取命令执行结果, 有两个结果: 正常的输出和错误的输出(PS: 子进程的输出就是主进程的输入)
BufferedReader bufrIn =
new BufferedReader(
new InputStreamReader(process.getInputStream(), "UTF-8")); // 正常的输出
log.info("====="+line+"\n");
while ((line = bufrIn.readLine()) != null) {
log.info("====="+line+"\n");
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
taskThreadPool.taskThreadPool().submit(new Runnable() {
@Override
public void run() {
// 读取输出
String line = null;
try {
// 获取命令执行结果, 有两个结果: 正常的输出和错误的输出(PS: 子进程的输出就是主进程的输入)
BufferedReader bufrError =
new BufferedReader(
new InputStreamReader(process.getErrorStream(), "UTF-8")); // 错误的输出
log.info("====="+line+"\n");
while ((line = bufrError.readLine()) != null) {
log.info("====="+line+"\n");
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
log.info("=========》》执行文件生成脚本结果:" + exitVal);
return exitVal;
}
这样就看到了我们程序卡住的原因居然是权限问题。。。。。。。
这样处理以后也能看到执行shell中具体的执行日志了,也能更好的排查问题。