java中Runtime.getRuntime().exec是用来执行外部命令的,我就是用这个来执行spark程序spark-submit,我的场景就是定时任务调用这个代码来执行spark任务,但是总是遇到Runtime.getRuntime().exec一直等待不结束,导致程序不往下执行,定时任务的线程也一直卡着不动。
1.阻塞原因分析
用jps命令查看到了阻塞到的进程,再用jstack查看进程信息,截取了最后的信息,因为jstack是从后往前分析的。
# jps 8023 SparkSubmit #jstack 8023 "main" #1 prio=5 os_prio=0 tid=0x00007f66fc010000 nid=0x1f7f runnable [0x00007f6705ba6000] java.lang.Thread.State: RUNNABLE at java.io.FileOutputStream.writeBytes(Native Method) at java.io.FileOutputStream.write(FileOutputStream.java:326) at java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:82) at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:140) - locked <0x00000005cbc49dc0> (a java.io.BufferedOutputStream) at java.io.PrintStream.write(PrintStream.java:482) - locked <0x00000005cbc49da0> (a java.io.PrintStream)
貌似是读取信息流导致的阻塞。
2.结合网上的信息是"错误输出流"和"错误输出流"导致的阻塞
具体原因-java程序给进程的输出流分配的缓冲区是很小的,有时候当进程输出信息很大的时候回导致缓冲区被填满,如果不及时处理程序会阻塞。如果程序没有对进程的输出流处理的会就会导致执行exec()的线程永远阻塞
3.解决问题
2.1第一种方法 命令行指定输出
spark-submit --class org.apache.spark.examples.SparkPi /opt/hadoop/spark-2.3.0-bin-hadoop2.7/examples/jars/spark-examples_2.11-2.3.0.jar 10 1>/dev/null 2>&1 &
这样就不需要你读取输出流,直接输出到不存在的地方,2>&1表示错误信息和前面的正常信息也一样输出到/dev/null。&表示后台运行
3.2第二种方法 用子进程读取输出流
读取输出流的多线程代码,可以公用重复使用,直接复制吧。
class InputStreamRunnable extends Thread { BufferedReader bReader = null; String type = null; public InputStreamRunnable(InputStream is, String _type) { try { bReader = new BufferedReader(new InputStreamReader(new BufferedInputStream(is), "UTF-8")); type = _type; } catch (Exception ex) { ex.printStackTrace(); } } public void run() { String line; int lineNum = 0; try { while ((line = bReader.readLine()) != null) { lineNum++; System.out.println(type+":"+line); } bReader.close(); } catch (Exception ex) { ex.printStackTrace(); } } }
然后修改Runtime.getRuntime().exec代码
Process process = Runtime.getRuntime().exec(commond.toString()); new InputStreamRunnable(process.getErrorStream(), "Error").start(); new InputStreamRunnable(process.getInputStream(), "Info").start(); boolean code = process.waitFor(30,TimeUnit.MINUTES); logger.warn("spark 执行计算结果:code-" + code); process.destroy();
再次测试发现所有的信息都打印出来了,阻塞问题也没有了。