繁体   English   中英

从多个 shell 子进程(Python)进行非阻塞实时读取

[英]Non-blocking realtime read from multiple shell subprocesses (Python)

我正在使用ffmpegsubrocess构建实时多视频流监控。 我目前有以下代码,其灵感来自“Async and await with subprocesses” post

问题是在一段时间后输出停止打印并且进程进入僵尸模式。 我猜这个问题与PIPE过载或死锁有关。 需要帮助。

"""Async and await example using subprocesses

Note:
    Requires Python 3.6.
"""

import os
import sys
import time
import platform
import asyncio

async def run_command_shell(command):
    """Run command in subprocess (shell)

    Note:
        This can be used if you wish to execute e.g. "copy"
        on Windows, which can only be executed in the shell.
    """
    # Create subprocess
    process = await asyncio.create_subprocess_shell(
        command,
        stderr=asyncio.subprocess.PIPE)

    # Status
    print('Started:', command, '(pid = ' + str(process.pid) + ')')

    # Wait for the subprocess to finish
    stdout, stderr = await process.communicate()

    # Progress
    if process.returncode == 0:
        print('Done:', command, '(pid = ' + str(process.pid) + ')')
    else:
        print('Failed:', command, '(pid = ' + str(process.pid) + ')')

    # Result
    result = stderr.decode().strip()

    # Real time print
    print(result)

    # Return stdout
    return result


def make_chunks(l, n):
    """Yield successive n-sized chunks from l.

    Note:
        Taken from https://stackoverflow.com/a/312464
    """
    if sys.version_info.major == 2:
        for i in xrange(0, len(l), n):
            yield l[i:i + n]
    else:
        # Assume Python 3
        for i in range(0, len(l), n):
            yield l[i:i + n]


def run_asyncio_commands(tasks, max_concurrent_tasks=0):
    """Run tasks asynchronously using asyncio and return results

    If max_concurrent_tasks are set to 0, no limit is applied.

    Note:
        By default, Windows uses SelectorEventLoop, which does not support
        subprocesses. Therefore ProactorEventLoop is used on Windows.
        https://docs.python.org/3/library/asyncio-eventloops.html#windows
    """

    all_results = []

    if max_concurrent_tasks == 0:
        chunks = [tasks]
    else:
        chunks = make_chunks(l=tasks, n=max_concurrent_tasks)

    for tasks_in_chunk in chunks:
        if platform.system() == 'Windows':
            loop = asyncio.ProactorEventLoop()
            asyncio.set_event_loop(loop)
        else:
            loop = asyncio.get_event_loop()

        commands = asyncio.gather(*tasks_in_chunk)  # Unpack list using *
        results = loop.run_until_complete(commands)
        all_results += results
        loop.close()
    return all_results


if __name__ == '__main__':

    start = time.time()

    if platform.system() == 'Windows':
        # Commands to be executed on Windows
        commands = [
            ['hostname']
        ]
    else:
        # Commands to be executed on Unix
        commands = [
            ['du', '-sh', '/var/tmp'],
            ['hostname'],
        ]
    cmds = [["ffmpeg -y -i udp://xxx.xx.xx.xxx:xxxx  -f null -"],
            ["ffmpeg -y -i udp://xxx.xx.xx.xxx:xxxx  -f null -"],
            ["ffmpeg -y -i udp://xxx.xx.xx.xxx:xxxx -f null -"],
            ["ffmpeg -y -i udp://xxx.xx.xx.xxx:xxxx  -f null -"],
            ["ffmpeg -y -i udp://xxx.xx.xx.xxx:xxxx -f null -"],
            ["ffmpeg -y -i udp://xxx.xx.xx.xxx:xxxx  -f null -"],
            ["ffmpeg -y -i udp://xxx.xx.xx.xxx:xxxx  -f null -"],
            ["ffmpeg -y -i udp://xxx.xx.xx.xxx:xxxx  -f null -"],
            ["ffmpeg -y -i udp://xxx.xx.xx.xxx:xxxx -f null -"],
            ["ffmpeg -y -i udp://xxx.xx.xx.xxx:xxxx -f null -"],
            ["ffmpeg -y -i udp://xxx.xx.xx.xxx:xxxx -f null -"],
            ["ffmpeg -y -i udp://xxx.xx.xx.xxx:xxxx -f null -"]]

    tasks = []
    for command in cmds:
        tasks.append(run_command_shell(*command))


    # # Shell execution example
    # tasks = [run_command_shell('copy c:/somefile d:/new_file')]

    # # List comprehension example
    # tasks = [
    #     run_command(*command, get_project_path(project))
    #     for project in accessible_projects(all_projects)
    # ]

    results = run_asyncio_commands(tasks, max_concurrent_tasks=20)  # At most 20 parallel tasks
    print('Results:', results)

    end = time.time()
    rounded_end = ('{0:.4f}'.format(round(end-start,4)))
    print('Script ran in about', str(rounded_end), 'seconds')

相关: 从多个子进程(Python)的非阻塞读取

原来问题很可能与通过多线程asyncio等进行的代码优化无关

原因可能是服务器限制,例如最大打开文件数/文件描述符 (FD)、防火墙、其他配置文件。

如果您偶然发现了类似的问题:


安装 htop

Htop是 Linux/Unix 类系统的交互式实时进程监控应用程序,也是top命令的便捷替代品,top命令是所有 Linux 操作系统上预装的默认进程监控工具。

这可能有助于澄清原因。


测试单个 ffmpeg 命令

正如 jfs 所说,我需要Minimal, Complete, and Verifiable example 所以我们从一个非常小的开始:测试一个过程。

ffmpeg -y -i udp://224.10.0.123:1234  -f null -

就我而言,结果证明任何多播都会在 2:10 - 2:20 分钟内挂起。 进程活着但处于僵尸模式。 这很奇怪,因为几天前一切正常。


测试另一个软( VLC 的 multicat

Multicat 的最新官方版本编号为 2.2,可在此处获取

得到它,不要忘记,需要在构建时安装biTStream

使用命令检查流以从流中录制视频:

timeout 10 multicat -uU @224.10.0.123:1234 test.ts

就我而言,同样的事情发生在第 2 分钟。 命令没有停止执行,但文件停止记录。


检查最大打开文件数/文件描述符更多信息

使用以下命令命令显示最大打开文件描述符数:

cat /proc/sys/fs/file-max

要查看硬值和软值,请发出如下命令:

ulimit -Hn
ulimit -Sn

在执行我的一个 python 脚本的某个时候,我看到了类似的错误,但增加这个参数并没有帮助我。


概括

所以问题与我的脚本的并行执行无关。 在另一台虚拟机上验证成功。 我联系了设置这个虚拟机的人,向他解释说最近几天出了点问题,建议问题出在防火墙上。 他说他没有碰任何东西。 但是在这个电话之后,一切都开始完美地工作了。 (我几乎可以肯定他打破了它) :D

GL大家!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM