利用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如何为图片添加水印
Nov 25 Python
Python二叉树定义与遍历方法实例分析
May 25 Python
对python3 一组数值的归一化处理方法详解
Jul 11 Python
Flask入门之上传文件到服务器的方法示例
Jul 18 Python
Python的高阶函数用法实例分析
Apr 11 Python
Python 监测文件是否更新的方法
Jun 10 Python
PyQt QListWidget修改列表项item的行高方法
Jun 20 Python
Python3.x+迅雷x 自动下载高分电影的实现方法
Jan 12 Python
python实现全排列代码(回溯、深度优先搜索)
Feb 26 Python
python生成大写32位uuid代码
Mar 03 Python
python判断元素是否存在的实例方法
Sep 24 Python
python中Pexpect的工作流程实例讲解
Mar 02 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 session机制
2011/07/17 PHP
PHP生成随机密码类分享
2014/06/25 PHP
smarty内置函数foreach用法实例
2015/01/22 PHP
js使用数组判断提交数据是否存在相同数据
2013/11/27 Javascript
form表单action提交的js部分与html部分
2014/01/07 Javascript
jquery xMarquee实现文字水平无缝滚动效果
2014/04/29 Javascript
基于JavaScript实现类似于百度学术高级检索功能
2016/03/02 Javascript
Angular.js回顾ng-app和ng-model使用技巧
2016/04/26 Javascript
JS实现左右无缝轮播图代码
2016/05/01 Javascript
微信小程序 登陆流程详细介绍
2017/01/17 Javascript
WebSocket实现简单客服聊天系统
2017/05/12 Javascript
Angular2入门教程之模块和组件详解
2017/05/28 Javascript
基于express中路由规则及获取请求参数的方法
2018/03/12 Javascript
python中global用法实例分析
2015/04/30 Python
浅谈python中set使用
2016/06/30 Python
Python3中使用urllib的方法详解(header,代理,超时,认证,异常处理)
2016/09/21 Python
Python数据结构与算法之二叉树结构定义与遍历方法详解
2017/12/12 Python
对Python3.6 IDLE常用快捷键介绍
2018/07/16 Python
Pandas0.25来了千万别错过这10大好用的新功能
2019/08/07 Python
python3 tkinter实现添加图片和文本
2019/11/26 Python
Java爬虫技术框架之Heritrix框架详解
2020/07/22 Python
如何用Python 加密文件
2020/09/10 Python
斯洛伐克香水和化妆品购物网站:Parfemy-Elnino.sk
2020/01/28 全球购物
信息技术专业大学生个人的自我评价
2013/10/05 职场文书
学生学习总结的自我评价
2013/10/22 职场文书
关于毕业的中学校园广播稿
2014/01/26 职场文书
优秀教师工作感言
2014/02/16 职场文书
考试作弊被抓检讨书
2014/10/02 职场文书
2014年内勤工作总结
2014/11/24 职场文书
交通肇事罪辩护词
2015/05/21 职场文书
病假条格式范文
2015/08/17 职场文书
go语言-在mac下brew升级golang
2021/04/25 Golang
Python Django获取URL中的数据详解
2021/11/01 Python
Python实现猜拳与猜数字游戏的方法详解
2022/04/06 Python
Java8 Stream API 提供了一种高效且易于使用的处理数据的方式
2022/04/13 Java/Android
清空 Oracle 安装记录并重新安装
2022/04/26 Oracle