使用子进程启动hadoop作业但无法从stdout获取日志

时间:2014-12-25 07:33:09

标签: python hadoop mapreduce subprocess

为了简化我的问题,这里是一个python脚本:

from subprocess import Popen, PIPE
proc = Popen(['./mr-task.sh'], shell=True, stdout=PIPE, stderr=PIPE)
while True:
    out = proc.stdout.readline()
    print(out)

这是mr-task.sh,它启动了mapreduce工作:

hadoop jar xxx.jar some-conf-we-don't-need-to-care

当我运行./mr-task时,我可以在屏幕上看到日志,例如:

14/12/25 14:56:44 INFO util.NativeCodeLoader: Loaded the native-hadoop library
14/12/25 14:56:44 INFO snappy.LoadSnappy: Snappy native library loaded
14/12/25 14:57:01 INFO mapred.JobClient: Running job: job_201411181108_16380
14/12/25 14:57:02 INFO mapred.JobClient:  map 0% reduce 0%
14/12/25 14:57:28 INFO mapred.JobClient:  map 100% reduce 0%

但我无法让这些输出运行python脚本。我尝试删除shell=True或获取stderr,仍然没有任何内容。

有没有人知道为什么会这样?

2 个答案:

答案 0 :(得分:3)

您可以将stderr重定向到stdout:

from subprocess import Popen, PIPE, STDOUT

proc = Popen(['./mr-task.sh'], stdout=PIPE, stderr=STDOUT, bufsize=1)
for line in iter(proc.stdout.readline, b''):
    print line,
proc.stdout.close()
proc.wait()

请参阅Python: read streaming input from subprocess.communicate()


  

在我的真实程序中,我将stderr重定向到stdout并从stdout读取,因此不需要bufsize,是吗?

将stderr重定向到stdout和bufsize是不相关的。更改bufsize可能会影响时间性能(默认bufsize = 0,即在Python 2上无缓冲)。无缓冲I / O可能慢10到100倍。像往常一样,如果重要的话,你应该衡量时间表现。

  

在子进程终止后调用Popen.wait / communication只是为了清除僵尸进程,这两种方法在这种情况下没有区别,对吗?

不同之处在于proc.communicate()在收割子进程之前关闭了管道。它释放文件描述符(有限资源)以供程序中的其他文件使用。

  

关于缓冲区,如果输出填充缓冲区maxsize,子进程会挂起吗?这是否意味着如果我使用默认的bufsize = 0设置我需要尽快从stdout读取,以便子进程不阻塞?

没有。这是一个不同的缓冲区。 bufsize控制调用.readline()方法时填充/排空的父级内的缓冲区。无论bufsize是什么,都不会出现僵局。

无论孩子可能产生多少输出,代码(如上所述)都不会死锁。

@falsetru's answer中的代码可能会死锁,因为它会创建两个管道(stdout=PIPE, stderr=PIPE),但它只能从一个管道中读取{{1 }})。

在子节点和父节点之间有几个缓冲区,例如,C stdio的stdout缓冲区(子进程内的libc缓冲区,父节点无法访问),子节点的stdout OS管道缓冲区(内核,父进程可以从中读取数据)这里)。这些缓冲区是固定的,如果您将更多数据放入其中,它们将不会增长。如果stdio的缓冲区溢出(例如,在proc.stderr调用期间),则数据被下游推送到子节点的stdout OS管道缓冲区中。如果没有人从管道读取然后该OS管道缓冲区填满并且子块阻塞(例如,在printf()系统调用上)尝试刷新数据。

具体来说,我假设是基于C stdio的程序和POSIXy操作系统。

发生死锁是因为父级尝试读取空的stderr管道,因为孩子正忙着尝试刷新其标准输出。因此,这两个过程都会挂起。

答案 1 :(得分:0)

一个可能的原因是输出打印到标准错误而不是标准输出。

尝试将stdout替换为stderr

from subprocess import Popen, PIPE
proc = Popen(['./mr-task.sh'], stdout=PIPE, stderr=PIPE)
while True:
    out = proc.stderr.readline()  # <----
    if not out:
        break
    print(out)