利用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 相关文章推荐
Python3.x和Python2.x的区别介绍
Feb 12 Python
Python群发邮件实例代码
Jan 03 Python
Python中让MySQL查询结果返回字典类型的方法
Aug 22 Python
python变量不能以数字打头详解
Jul 06 Python
python实现对文件中图片生成带标签的txt文件方法
Apr 27 Python
PIL对上传到Django的图片进行处理并保存的实例
Aug 07 Python
浅谈pandas.cut与pandas.qcut的使用方法及区别
Mar 03 Python
python搜索算法原理及实例讲解
Nov 18 Python
手把手教你怎么用Python实现zip文件密码的破解
May 27 Python
基于Python实现将列表数据生成折线图
Mar 23 Python
Python调用腾讯API实现人脸身份证比对功能
Apr 04 Python
PyCharm 配置SSH和SFTP连接远程服务器
May 11 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
sony ICF-2010 拆解与改装
2021/03/02 无线电
PHP模板引擎SMARTY
2006/10/09 PHP
发挥语言的威力--融合PHP与ASP
2006/10/09 PHP
PHP中使用hidef扩展代替define提高性能
2015/04/09 PHP
PHP+Ajax实时自动检测是否联网的方法
2015/07/01 PHP
PHP魔术方法使用方法汇总
2016/02/14 PHP
Yii2框架实现数据库常用操作总结
2017/02/08 PHP
yii2使用gridView实现下拉列表筛选数据
2017/04/10 PHP
PHP 面向对象程序设计之类属性与类常量实现方法分析
2020/04/13 PHP
jquery 全局AJAX事件使用代码
2010/11/05 Javascript
Javascript 面试题随笔
2011/03/31 Javascript
用JQuery在网页中实现分隔条功能的代码
2012/08/09 Javascript
indexOf 和 lastIndexOf 使用示例介绍
2014/09/02 Javascript
AngularJS基础 ng-list 指令详解及示例代码
2016/08/02 Javascript
jquery 动态增加删除行的简单实例(推荐)
2016/10/12 Javascript
从Vuex中取出数组赋值给新的数组,新数组push时报错的解决方法
2018/09/18 Javascript
JavaScript Blob对象原理及用法详解
2020/10/14 Javascript
[03:00]DOTA2-DPC中国联赛1月18日Recap集锦
2021/03/11 DOTA
pydev使用wxpython找不到路径的解决方法
2013/02/10 Python
Python线程指南详细介绍
2017/01/05 Python
python实现手机通讯录搜索功能
2018/02/22 Python
Python骚操作之动态定义函数
2019/03/26 Python
Python实现的删除重复文件或图片功能示例【去重】
2019/04/23 Python
Flask框架 CSRF 保护实现方法详解
2019/10/30 Python
python socket 聊天室实例代码详解
2019/11/14 Python
基于Python实现扑克牌面试题
2019/12/11 Python
Python截图并保存的具体实例
2021/01/14 Python
Nike瑞典官方网站:Nike.com (SE)
2018/11/26 全球购物
企划经理的岗位职责
2013/11/17 职场文书
护士自荐信范文
2013/12/15 职场文书
毕业生写求职信的要点
2014/03/04 职场文书
道路运输企业安全生产责任书
2014/07/28 职场文书
2015年前台个人工作总结
2015/04/03 职场文书
安全教育主题班会教案
2015/08/12 职场文书
家庭教育教师培训学习体会
2016/01/14 职场文书
员工给公司的建议书
2019/06/24 职场文书