利用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 相关文章推荐
简述Python中的面向对象编程的概念
Apr 27 Python
解决python给列表里添加字典时被最后一个覆盖的问题
Jan 21 Python
Python使用pandas和xlsxwriter读写xlsx文件的方法示例
Apr 09 Python
python实现按关键字筛选日志文件
Dec 24 Python
jupyter notebook 添加kernel permission denied的操作
Apr 21 Python
什么是Python中的匿名函数
Jun 02 Python
Python with语句用法原理详解
Jul 03 Python
利用Python发送邮件或发带附件的邮件
Nov 12 Python
五种Python转义表示法
Nov 27 Python
Python-split()函数实例用法讲解
Dec 18 Python
Python基于Opencv识别两张相似图片
Apr 25 Python
Django项目如何获得SSL证书与配置HTTPS
Apr 30 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
ThinkPHP空模块和空操作详解
2014/06/30 PHP
PHP如何通过传引用的思想实现无限分类(代码简单)
2015/10/13 PHP
什么是PHP文件?如何打开PHP文件?
2017/06/27 PHP
Bookmarklet实现启动jQuery(模仿 云输入法)
2010/09/15 Javascript
jquery jqPlot API 中文使用教程(非常强大的图表工具)
2011/08/15 Javascript
javascript+xml实现简单图片轮换(只支持IE)
2012/12/23 Javascript
JavaScript的21条基本知识点
2014/03/04 Javascript
一款基jquery超炫的动画导航菜单可响应单击事件
2014/11/02 Javascript
深入分析Javascript事件代理
2016/01/30 Javascript
JS两个数组比较,删除重复值的巧妙方法(推荐)
2016/06/03 Javascript
使用JavaScript开发跨平台的桌面应用详解
2017/07/27 Javascript
mint-ui在vue中的使用示例
2018/04/05 Javascript
angular4强制刷新视图的方法
2018/10/09 Javascript
Vue cli3 库模式搭建组件库并发布到 npm的流程
2018/10/12 Javascript
JavaScript Date对象功能与用法学习记录
2020/04/28 Javascript
Python Mysql自动备份脚本
2008/07/14 Python
深入解析Python中的上下文管理器
2016/06/28 Python
Python实现批量检测HTTP服务的状态
2016/10/27 Python
Django实战之用户认证(初始配置)
2018/07/16 Python
Django之创建引擎索引报错及解决详解
2019/07/17 Python
使用Windows批处理和WMI设置Python的环境变量方法
2019/08/14 Python
pywinauto自动化操作记事本
2019/08/26 Python
python3 BeautifulSoup模块使用字典的方法抓取a标签内的数据示例
2019/11/28 Python
python 微信好友特征数据分析及可视化
2020/01/07 Python
python 装饰器功能与用法案例详解
2020/03/06 Python
个人收入证明范本
2014/01/12 职场文书
面试后的感谢信范文
2014/02/01 职场文书
学生手册评语
2014/05/05 职场文书
艺术设计专业毕业生推荐信
2014/07/08 职场文书
私人房屋买卖协议书
2014/10/04 职场文书
4S店收银员岗位职责
2015/04/07 职场文书
企业催款函范本
2015/06/24 职场文书
公安纪律作风整顿心得体会
2016/01/23 职场文书
如何写好闭幕词
2019/04/02 职场文书
python实现ROA算子边缘检测算法
2021/04/05 Python
CSS 文字装饰 text-decoration & text-emphasis 详解
2021/04/06 HTML / CSS