Python - FastAPI + SubProcess 手机端实时启动任务

本文介绍如何通过Redis队列与FastAPI接口配合,实现实时监控任务状态并远程启动,简化服务器管理。通过手机访问接口,轻松控制定时任务的执行,提高运维效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一.引言

工程实践中,经常遇到任务故障或其他原因导致任务执行失败,常见的方法是设计 Cron Protect 脚本监控任务执行,但是如果任务执行超时但不退出 Cron Protect 则不会生效,这时候需要强制 Kill 掉任务并重启,但是每次都需要登录服务器,非常的麻烦,这时候可以通过设计接口 + 监听程序,实现程序的自动化启动,只需手机访问接口即可。

二.实践

经过上面的分析,该监控的功能主要涉及到监控接口参数和实现启动任务,这里需要下述3个工具:

=> 监听状态: Redis

实时监控接口传入的最新值,这里采用 Redis 队列实现

=> 接口:

接口通过 Python 的 FastAPI 简洁实现

=> 启动任务:

Python 启动 sh 任务可以通过 SubProcess 实现

1.监听状态 + 启动任务

由于 Redis 监听状态与重启任务是两个连续的步骤,即监听到启动状态随后执行启动任务操作,所以这两个放到一起实现,当然不使用 Redis 也可以,可以使用任意的存储端实现对状态值的传递。

#coding=utf-8
import redis
import subprocess
import datetime
import time

# 初始化客户端
r = redis.Redis(host='your_host', port='your_port', decode_responses=True)
# 其他管道
# client = new MyClient(host, port)

# 队列 Key
read_key = 'value_state'

def getValue():
    now = datetime.datetime.now()
    length = r.llen(read_key)
    # 只保留最新 并清空队列
    r.ltrim(read_key,0,0)
    value = r.rpop(read_key)
    curLength = r.llen(read_key)
    print("time:%s value: %s oriLength:%s curLength:%s" % (now, value, length, curLength))
    if value == "self_defined":
        log = subprocess.Popen("nohup sh test.sh  > log 2>&1 &", stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
        print("Application Started At:", now, log.communicate()[0].strip())

while(1):
    getValue()
    time.sleep(10)

A.getValue() 函数负责从 Redis 队列中获取最新的值并清除当前队列中多余的值,所以执行了 r.ltrim 操作,这样避免接口异常传入过多状态值而导致任务多次重启

B.rpop 返回最新元素,根据元素的值与自定义的值可以决定是否 subprocess.Popen 吊起自己的任务,这里 Popen 是异步执行,所以不会导致 python 程序同步等待,如果需要监听执行任务的状态,则可以修改 nohup 的提交方式,这样 log.communicate() 会堵塞操作等待 test.sh 执行完毕并判断任务执行状态

C.通过 While True 实现监听系统的 7x24 服务,这里 sleep 设置每次监听的 interval

2.接口

python 实现简单接口可以通过 FastAPI,方便高效,接口只需要实现一个基本 Get 功能即可,通过手机移动端就可以把参数传给 Redis,从而监听程序获取最新状态值

#!/usr/bin/python
# -*- coding: UTF-8 -*-

from fastapi import FastAPI
from redis import Redis

send = Redis(host='your_host', port='your_port')
app = FastAPI()


@app.get("/monitor/{value}")
async def send_info(value: str = None):
    send.lpush("value_state", value)
    return {"value": value}

可以通过 uvicorn 部署到自己的服务器上,也可以使用 Netstat 部署 ,reload 代表更新代码随时更新该 APP

# uvicorn Monitor:app --reload

可以直接通过 URL 传参: 

http://127.0.0.1:8000/monitor/self_difined

也可以进入交互界面传参:

 http://127.0.0.1:8000/docs

3.测试

先构建一个测试脚本 test.sh:

#!/bin/bash

echo `date` 任务开始...

然后 nohup 挂起 python 程序并在服务器部署接口就可以手机端控制任务执行了,任务每隔 10 s进行一次监控,可以查看 log 日志看到 test.sh 的日志:

Thu Sep 16 10:21:04 CST 2021 任务开始...

 10:21:04 与我们接口日志的 Application Start 时间吻合,执行重启任务后队列清空,持续监控

 后续需要重启任务不再需要登录服务器,只需登录手机访问:

 http://127.0.0.1:8000/monitor/self_difined 即可完成随时唤醒任务,非常的奈斯

<think>嗯,用户决定使用Web方案来部署一个自用的视频处理工具在自己的笔记本上。首先,我需要确认用户的需求。用户之前已经得到了关于使用Qt或Web的方案建议,现在他们选择了Web,并且是个人自用,部署在本地。这意味着用户可能更倾向于一个轻量级的解决方案,不需要复杂的服务器配置,但需要确保本地运行的稳定性和便捷性。 接下来,我需要考虑用户可能的技术背景。他们可能有一定的Python基础,但对Web开发可能不太熟悉。因此,在步骤分解时需要详细说明每个部分,尤其是前后端的交互和FFmpeg的集成。同时,用户是自用,所以安全性和多用户支持可能不是重点,但需要确保界面友好和操作简便。 用户的需求是实现视频的截取和合并,使用Web界面。因此,项目需要分为前端和后端。前端负责用户界面,上传文件,设置参数;后端处理视频处理任务,调用FFmpeg。需要确保文件上传的路径处理正确,避免文件覆盖或存储混乱。同时,由于是本地部署,需要考虑文件系统的权限和路径问题。 接下来是技术选型。用户已经选择Web方案,所以需要确定前端和后端的具体技术。前端可以选择Vue或React,但考虑到用户可能希望简单快捷,或许使用基本的HTML/CSS/JavaScript加上一些轻量级框架如Bootstrap会更合适。后端使用Flask,因为Python集成方便,且Flask轻量适合小型项目。 然后,环境准备方面,用户需要安装FFmpeg并添加到系统路径,确保Python环境正确,安装必要的依赖如Flask、moviepy等。需要注意的是,虽然用户可能已经安装了FFmpeg,但可能需要确认版本兼容性和路径是否正确,避免运行时出现找不到FFmpeg的错误。 项目结构方面,需要分模块组织代码,例如核心处理模块、前端静态文件、模板、上传目录等。上传目录需要处理临时文件和结果文件,定期清理以避免占用过多磁盘空间。同时,错误处理是关键,特别是在调用子进程运行FFmpeg时,需要捕获异常并反馈给前端,避免界面卡死或无响应。 在前后端交互部分,文件上传需要使用表单或者AJAX,后端接收文件并保存到指定目录。处理视频时可能需要异步任务,避免阻塞前端请求。由于是个人使用,可能不需要复杂的任务队列,但需要考虑长时间任务的处理,比如使用线程或Flask的异步支持。 安全方面,虽然是个自用项目,但仍需注意文件上传的安全,限制文件类型,防止恶意文件上传。另外,临时文件的清理机制也很重要,避免积累大量无用文件。 部署方面,用户需要在本地运行Flask应用,可能希望开机自启或方便地启动。可以考虑使用批处理脚本或Docker容器化,但考虑到用户可能希望简单,提供基本的运行指令即可,如flask run命令。 最后,测试和优化。用户需要确保各功能正常,特别是大文件处理和错误情况下的表现。进度反馈可能通过简单的日志或前端提示,不需要复杂的进度条,但需要用户知道处理状态。性能方面,可以考虑多线程处理,但需注意资源竞争和稳定性。 总结来说,用户需要一个结构清晰、步骤详细、易于本地部署的Web方案,重点在于前后端的交互、FFmpeg的调用、错误处理和文件管理。需要避免复杂的配置,确保用户能够顺利运行,并在出现问题时能快速定位和解决。</think>我将为您详细说明基于Web方案的本地部署准备和实现步骤,并提供完整的实现路线图: --- ### **一、准备工作清单** ```mermaid graph LR A[环境准备] --> B[安装FFmpeg] A --> C[Python环境] A --> D[代码编辑器] B --> |验证安装| E[终端执行 ffmpeg -version] C --> |推荐版本| F[Python 3.8+] C --> |安装库| G["pip install flask moviepy python-dotenv"] ``` 1. **必需软件** - FFmpeg:到官网下载编译版并配置环境变量 - Python 3.8+:建议使用虚拟环境(推荐`venv`) - 现代浏览器:Chrome/Firefox最新版 2. **项目依赖** ```bash pip install flask moviepy python-dotenv watchdog ``` --- ### **二、项目实现步骤** #### **步骤1:创建基础项目结构** ``` video-tool/ ├── app.py # Flask主程序 ├── uploads/ # 文件存储目录 │ ├── temp/ # 临时文件 │ └── results/ # 处理结果 ├── static/ # 前端资源 │ ├── css/ │ ├── js/ │ └── lib/ # 第三方库(如jQuery) ├── templates/ # HTML模板 │ ├── index.html │ └── progress.html └── .env # 环境配置 ``` #### **步骤2:编写核心后端(Flask示例)** ```python # app.py from flask import Flask, request, render_template import os from datetime import datetime import subprocess app = Flask(__name__) app.config['UPLOAD_FOLDER'] = 'uploads/temp' app.config['MAX_CONTENT_LENGTH'] = 1024 * 1024 * 1024 # 限制1GB @app.route('/') def index(): return render_template('index.html') @app.route('/process', methods=['POST']) def process_video(): # 接收参数 file = request.files['video'] start = request.form['start_time'] duration = request.form['duration'] # 生成唯一文件名 timestamp = datetime.now().strftime("%Y%m%d%H%M%S") input_path = os.path.join(app.config['UPLOAD_FOLDER'], f"input_{timestamp}.mp4") output_path = os.path.join('uploads/results', f"output_{timestamp}.mp4") # 保存上传文件 file.save(input_path) # 调用FFmpeg cmd = [ 'ffmpeg', '-y', # 覆盖已有文件 '-ss', str(start), # 开始时间 '-i', input_path, '-t', str(duration), # 持续时间 '-c:v', 'libx264', # 编码格式 '-preset', 'fast', # 编码速度 output_path ] try: subprocess.run(cmd, check=True, stderr=subprocess.PIPE) return {'status': 'success', 'output': output_path} except subprocess.CalledProcessError as e: return {'status': 'error', 'message': e.stderr.decode()}, 500 ``` #### **步骤3:开发前端界面(HTML示例)** ```html <!-- templates/index.html --> <!DOCTYPE html> <html> <head> <title>视频处理工具</title> <link rel="stylesheet" href="{{ url_for('static', filename='css/bootstrap.min.css') }}"> </head> <body> <div class="container mt-5"> <h2>视频处理工具</h2> <!-- 文件上传表单 --> <form id="uploadForm" enctype="multipart/form-data"> <div class="mb-3"> <input type="file" class="form-control" name="video" accept="video/*"> </div> <div class="row"> <div class="col"> <input type="number" class="form-control" name="start_time" placeholder="开始时间(秒)"> </div> <div class="col"> <input type="number" class="form-control" name="duration" placeholder="持续时间(秒)"> </div> </div> <button type="submit" class="btn btn-primary mt-3">开始处理</button> </form> <!-- 进度显示区域 --> <div id="progress" class="mt-3" style="display:none;"> <div class="progress"> <div class="progress-bar" style="width: 0%"></div> </div> <p class="text-muted mt-2">处理中...</p> </div> </div> <script src="{{ url_for('static', filename='js/jquery.min.js') }}"></script> <script> // AJAX提交处理请求 $('#uploadForm').submit(function(e) { e.preventDefault(); $('#progress').show(); let formData = new FormData(this); $.ajax({ url: '/process', type: 'POST', data: formData, contentType: false, processData: false, success: function(response) { alert('处理完成!文件路径:' + response.output); }, error: function(xhr) { alert('处理失败:' + xhr.responseJSON.message); }, complete: function() { $('#progress').hide(); } }); }); </script> </body> </html> ``` --- ### **三、关键配置项** 1. **环境变量配置(.env)** ```ini FLASK_ENV=development SECRET_KEY=your_random_string FFMPEG_PATH=/usr/local/bin/ffmpeg # 修改为实际路径 ``` 2. **启动脚本(run.sh)** ```bash #!/bin/bash export FLASK_APP=app.py flask run --host=0.0.0.0 --port=5000 ``` --- ### **四、运行与测试** 1. **启动服务** ```bash chmod +x run.sh ./run.sh ``` 2. **访问地址** - 本地访问:`http://localhost:5000` - 局域网访问:`http://[你的IP地址]:5000` 3. **测试场景** - 上传MP4/MOV格式视频 - 测试分段截取(建议先用小文件测试) - 检查输出文件完整性 --- ### **五、进阶优化建议** 1. **功能增强** - 添加视频预览功能(用`ffmpeg -i input.mp4 -vframes 1 thumbnail.jpg`生成缩略图) - 支持批量操作(使用`concurrent.futures.ThreadPoolExecutor`实现并发) 2. **稳定性提升** ```python # 添加文件清理任务 import schedule import time def clean_temp_files(): # 删除超过24小时的临时文件 pass schedule.every().day.at("03:00").do(clean_temp_files) # 在单独线程运行定时任务 import threading threading.Thread(target=lambda: schedule.run_continuously()).start() ``` 3. **部署便捷化** - 使用`pyinstaller`打包成可执行文件 - 编写Windows服务脚本(实现开机自启) --- ### **六、常见问题解决方案** 1. **FFmpeg路径问题** - Windows:检查环境变量是否包含`ffmpeg/bin`目录 - Mac/Linux:使用`which ffmpeg`验证安装位置 2. **大文件上传失败** - 修改Flask配置:`app.config['MAX_CONTENT_LENGTH'] = 3 * 1024 * 1024 * 1024`(3GB) 3. **浏览器兼容性问题** - 使用Fetch API替代jQuery AJAX - 添加Polyfill支持旧版浏览器 --- 选择Web方案的优势在于可以随时通过手机/平板访问服务(需在同一局域网),后续扩展远程访问功能也更为方便。建议先从基础版本开始实现,逐步添加功能模块。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BIT_666

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值