利用Python+Java调用Shell脚本时的死锁陷阱详解


Posted in Python onJanuary 24, 2018

前言

最近有一项需求,要定时判断任务执行条件是否满足并触发 Spark 任务,平时编写 Spark 任务时都是封装为一个 Jar 包,然后采用 Shell 脚本形式传入所需参数执行,考虑到本次判断条件逻辑复杂,只用 Shell 脚本完成不利于开发测试,所以调研使用了 Python 和 Java 分别调用 Spark 脚本的方法。

使用版本为 Python 3.6.4 及 JDK 8

Python

主要使用 subprocess 库。Python 的 API 变动比较频繁,在 3.5 之后新增了 run 方法,这大大降低了使用难度和遇见 Bug 的概率。

subprocess.run(["ls", "-l"])
subprocess.run(["sh", "/path/to/your/script.sh", "arg1", "arg2"])

为什么说使用 run 方法可以降低遇见 Bug 的概率呢?

在没有 run 方法之前,我们一般调用其他的高级方法,即 Older high-level API,比如 call,check_all,或者直接创建 Popen 对象。因为默认的输出是 console,这时如果对 API 不熟悉或者没有仔细看 doc,想要等待子进程运行完毕并获取输出,使用了 stdout = PIPE 再加上 wait 的话,当输出内容很多时会导致 Buffer 写满,进程就一直等待读取,形成死锁。在一次将 Spark 的 log 输出到 console 时,就遇到了这种奇怪的现象,下边的脚本可以模拟:

# a.sh
for i in {0..9999}; do
 echo '***************************************************'
done
p = subprocess.Popen(['sh', 'a.sh'], stdout=subprocess.PIPE)
p.wait()

而 call 则在方法内部直接调用了 wait 产生相同的效果。

要避免死锁,则必须在 wait 方法调用之前自行处理掉输入输出,或者使用推荐的 communicate 方法。 communicate 方法是在内部生成了读取线程分别读取 stdout stderr,从而避免了 Buffer 写满。而之前提到的新的 run 方法,就是在内部调用了 communicate。

stdout, stderr = process.communicate(input, timeout=timeout)

Java

说完了 Python,Java 就简单多了。

Java 一般使用 Runtime.getRuntime().exec() 或者 ProcessBuilder 调用外部脚本:

Process p = Runtime.getRuntime().exec(new String[]{"ls", "-al"});
Scanner sc = new Scanner(p.getInputStream());
while (sc.hasNextLine()) {
 System.out.println(sc.nextLine());
}
// or
Process p = new ProcessBuilder("sh", "a.sh").start(); 
p.waitFor(); // dead lock

需要注意的是:这里 stream 的方向是相对于主程序的,所以 getInputStream() 就是子进程的输出,而 getOutputStream() 是子进程的输入。

基于同样的 Buffer 原因,假如调用了 waitFor 方法等待子进程执行完毕而没有及时处理输出的话,就会造成死锁。
由于 Java API 很少变动,所以没有像 Python 那样提供新的 run 方法,但是开源社区也给出了自己的方案,如commons exec,或 http://www.baeldung.com/run-shell-command-in-java,或 alvin alexander 给出的方案(虽然不完整)。

// commons exec,要想获取输出的话,相比 python 来说要复杂一些
CommandLine commandLine = CommandLine.parse("sh a.sh");
  
ByteArrayOutputStream out = new ByteArrayOutputStream();
PumpStreamHandler streamHandler = new PumpStreamHandler(out);
  
Executor executor = new DefaultExecutor();
executor.setStreamHandler(streamHandler);
executor.execute(commandLine);
  
String output = new String(out.toByteArray());

但其中的思想和 Python 都是统一的,就是在后台开启新线程读取子进程的输出,防止 Buffer 写满。

另一个统一思想的地方就是,都推荐使用数组或 list 将输入的 shell 命令分隔成多段,这样的话就由系统来处理空格等特殊字符问题。

参考:

https://dcreager.net/2009/08/06/subprocess-communicate-drawbacks/ https://alvinalexander.com/java/java-exec-processbuilder-process-1 https://www.javaworld.com/article/2071275/core-java/when-runtime-exec—won-t.html

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

Python 相关文章推荐
NumPy 如何生成多维数组的方法
Feb 05 Python
Python实现正弦信号的时域波形和频谱图示例【基于matplotlib】
May 04 Python
使用 Python 实现文件递归遍历的三种方式
Jul 18 Python
详解Django的CSRF认证实现
Oct 09 Python
python 循环读取txt文档 并转换成csv的方法
Oct 26 Python
解决Python selenium get页面很慢时的问题
Jan 30 Python
python读取txt文件并取其某一列数据的示例
Feb 19 Python
python3+PyQt5 自定义窗口部件--使用窗口部件样式表的方法
Jun 26 Python
Python类如何定义私有变量
Feb 03 Python
详解python 降级到3.6终极解决方案
Feb 06 Python
Python实现检测文件的MD5值来查找重复文件案例
Mar 12 Python
python xlwt模块的使用解析
Apr 13 Python
python做量化投资系列之比特币初始配置
Jan 23 #Python
python在非root权限下的安装方法
Jan 23 #Python
Python解析命令行读取参数--argparse模块使用方法
Jan 23 #Python
Python 查看文件的读写权限方法
Jan 23 #Python
Python3 中文文件读写方法
Jan 23 #Python
Python3之文件读写操作的实例讲解
Jan 23 #Python
Python实现邮件的批量发送的示例代码
Jan 23 #Python
You might like
用PHP将数据导入到Foxmail的实现代码
2010/09/05 PHP
php获取网站百度快照日期的方法
2015/07/29 PHP
YII动态模型(动态表名)支持分析
2016/03/29 PHP
php 输入输出流详解及示例代码
2016/08/25 PHP
php中preg_replace正则替换用法分析【一次替换多个值】
2017/01/17 PHP
PHP分页显示的方法分析【附PHP通用分页类】
2018/05/10 PHP
解决laravel资源加载路径设置的问题
2019/10/14 PHP
PHP与Web页面的交互示例详解二
2020/08/04 PHP
JSONP 跨域共享信息
2012/08/16 Javascript
Javascript的setTimeout()使用闭包特性时需要注意的问题
2014/09/23 Javascript
jQuery实现的输入框选择时间插件用法实例
2015/02/28 Javascript
深入探讨javascript中的数据类型
2015/03/04 Javascript
编写高质量JavaScript代码的基本要点
2016/03/02 Javascript
基于MVC+EasyUI的web开发框架之使用云打印控件C-Lodop打印页面或套打报关运单信息
2016/08/29 Javascript
node.js学习之交互式解释器REPL详解
2016/12/08 Javascript
ES6新特性之字符串的扩展实例分析
2017/04/01 Javascript
详解如何在vue中使用sass
2017/06/21 Javascript
自定义Vue中的v-module双向绑定的实现
2019/04/17 Javascript
Vue实现数据表格合并列rowspan效果
2020/11/30 Javascript
js blob类型url的视频下载问题的解决
2019/11/29 Javascript
Python二维码生成库qrcode安装和使用示例
2014/12/16 Python
读取本地json文件,解析json(实例讲解)
2017/12/06 Python
使用python实现离散时间傅里叶变换的方法
2019/09/02 Python
Python QT组件库qtwidgets的使用
2020/11/02 Python
使用python将微信image下.dat文件解密为.png的方法
2020/11/30 Python
html5 Canvas绘制线条 closePath()实例代码
2012/05/10 HTML / CSS
挪威户外活动服装和装备购物网站:Bergfreunde挪威
2016/10/20 全球购物
开放系统互连参考模型
2016/06/29 面试题
单位介绍信范文
2014/01/18 职场文书
学雷锋活动总结范文
2014/04/25 职场文书
优质服务口号
2014/06/11 职场文书
“四风”查摆问题自我剖析材料
2014/09/27 职场文书
教师个人工作总结范文2014
2014/11/10 职场文书
北京颐和园导游词
2015/01/30 职场文书
建议书的格式及范文
2015/09/14 职场文书
幼儿园小班教学反思
2016/03/03 职场文书