【问题标题】:Python subprocess polling not giving return code when used with Java process与 Java 进程一起使用时,Python 子进程轮询不给出返回码
【发布时间】:2020-03-14 03:26:48
【问题描述】:

当进程完成时,子进程轮询不返回返回代码时遇到问题。

I found out how to set a timeout on subprocess.Popen 并将其用作我的代码的基础。但是,我有一个使用 Java 的调用,它没有正确报告返回代码,所以即使它实际上已经完成,每个调用都“超时”。我知道这个过程已经完成,因为当删除轮询超时检查时,调用运行没有问题,返回一个好的退出代码并且在时间限制内。

这是我正在测试的代码。

import subprocess
import time


def execute(command):
    print('start command: {}'.format(command))
    process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

    print('wait')
    wait = 10
    while process.poll() is None and wait > 0:
        time.sleep(1)
        wait -= 1
    print('done')

    if wait == 0:
        print('terminate')
        process.terminate()

    print('communicate')
    stdout, stderr = process.communicate()
    print('rc')
    exit_code = process.returncode
    if exit_code != 0:
        print('got bad rc')

if __name__ == '__main__':
    execute(['ping','-n','15','127.0.0.1']) # correctly times out
    execute(['ping','-n','5','127.0.0.1']) # correctly runs within the time limit

    # incorrectly times out
    execute(['C:\\dev\\jdk8\\bin\\java.exe', '-jar', 'JMXQuery-0.1.8.jar', '-url', 'service:jmx:rmi:///jndi/rmi://localhost:18080/jmxrmi', '-json', '-q', 'java.lang:type=Runtime;java.lang:type=OperatingSystem'])

您可以看到,两个示例设计为超时,两个示例不超时,它们都可以正常工作。但是,最后一个(使用jmxquery 获取 tomcat 指标)不返回退出代码,因此“超时”并且必须终止,然后导致它返回错误代码 1。

在子进程轮询与此 Java 进程交互的方式中,我是否缺少某些东西,导致它不返回退出代码?有没有办法让一个超时选项来处理这个问题?

【问题讨论】:

  • 该过程是否产生超过几千字节的输出?
  • @DavisHerring 当然可以,但我认为并非所有请求都可以。具体来说,实际脚本中有一大一小。
  • 小号是否按预期完成? (如果您只是跳过轮询并完全终止,它们会很快完成吗?)
  • @DavisHerring 是的,它们通常都会在一秒钟内完成,而无需进行轮询。但是我遇到了一个呼叫挂起的问题,所以现在我希望设置一个超时。尽管 process.poll 认为它仍在运行直到超时,但这两个调用都无法使用此超时。

标签: python python-2.7 subprocess


【解决方案1】:

这与许多现有问题的原因相同,但想要强制超时需要不同的答案。

操作系统故意只为每个管道提供少量的缓冲区空间。当一个进程写入一个已满的进程时(因为阅读器还没有消费之前的输出),它会阻塞。 (原因是生产者比其消费者快,否则将能够快速使用大量内存而没有任何收益。)因此,如果您想对子进程执行以下一项以上操作,则必须将它们交错而不是依次进行:

  1. 从标准输出读取
  2. 从标准错误中读取(除非它通过subprocess.STDOUT 合并)
  3. 等待进程退出,或等待超时结束

当然,子进程可能会在它退出之前关闭它的流,在你注意到超时之后和你杀死它之前写有用的输出,和/或启动额外的进程来无限期地保持管道打开,所以你可能想要多个超时。可能提供最多信息的是管道上的 EOF,所以重复使用类似 select 的东西来等待(不管剩下多少)超时,在准备好的流上发出 single reads,和wait(如果您担心在早期流关闭后挂起,则另一个超时)在 EOF 上。如果发生超时,则(尝试)终止子进程,并考虑发出非阻塞读取(或另一个超时循环)以在关闭管道之前获取任何最后可用的输出。

【讨论】:

    【解决方案2】:

    使用@DavisHerring 的另一个答案作为更多研究的基础,我遇到了一个适用于我原来案例的概念。这是由此产生的代码。

    import subprocess
    import threading
    import time
    
    
    def execute(command):
        print('start command: {}'.format(command))
        process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        timer = threading.Timer(10, terminate_process, [process])
        timer.start()
    
        print('communicate')
        stdout, stderr = process.communicate()
        print('rc')
        exit_code = process.returncode
        timer.cancel()
    
        if exit_code != 0:
            print('got bad rc')
    
    def terminate_process(p):
        try:
            p.terminate()
        except OSError:
            pass # ignore error
    

    它使用threading.Timer 来确保进程不会超过时间限制,如果超过则终止进程。否则它会等待响应并在完成后取消计时器。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-09-10
      • 1970-01-01
      • 2019-05-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多