使用来自 Python 的 PID 检查进程的状态

Checking the status of a process using its PID from Python

我用 Python 编写的 REST API 生成的进程大约需要 3 分钟才能完成。我将 PID 存储在一个全局数组中,并设置了一个辅助检查方法,该方法应确认进程是否仍在 运行,或者是否已完成。

我能找到的唯一方法是轮询子进程(我无法在此路由中访问它),或者尝试终止进程以查看它是否还活着。有没有什么干净的方法可以根据 PID 得到一个二进制答案,如果它仍然是 运行,如果不是,它是否成功完成?

from flask import Flask, jsonify, request, Response
from subprocess import Popen, PIPE
import os

app = Flask(__name__)

QUEUE_ID = 0

jobs = []

@app.route("/compile", methods=["POST"])
def compileFirmware():

    f = request.files['file']
    f.save(f.filename)

    os.chdir("/opt/src/2.0.x")

    process = Popen(['platformio', 'run', '-e', 'mega2560'], stdout=PIPE, stderr=PIPE, universal_newlines=True)

    global QUEUE_ID
    QUEUE_ID += 1

    data = {'id':QUEUE_ID, 'pid':process.pid}
    jobs.append(data)

    output, errors = process.communicate()
    print (output)
    print (errors)

    response = jsonify()
    response.status_code = 202 #accepted
    response.headers['location'] = '/queue/' + str(QUEUE_ID)
    response.headers.add('Access-Control-Allow-Origin', '*')
    return response

@app.route("/queue/<id>", methods=["GET"])
def getStatus(id):

    #CHECK PID STATUS HERE

    content = {'download_url': 'download.com'}
    response = jsonify(content)
    response.headers.add('Access-Control-Allow-Origin', '*')
    return response

if __name__ == '__main__':
    app.run(host='0.0.0.0',port=8080)

这是一个有效的小模拟:

from flask import Flask, jsonify, request, Response, abort
from subprocess import Popen, PIPE
import os

app = Flask(__name__)

QUEUE = { }

@app.route("/compile", methods=["POST"])
def compileFirmware():
    process = Popen(['python','-c','"import time; time.sleep(300)"'], stdout=PIPE, stderr=PIPE, universal_newlines=True)
    QUEUE[str(process.pid)] = process # String because in GET the url param will be interpreted as str
    response = jsonify()
    response.status_code = 202 #accepted
    response.headers['location'] = '/queue/' + str(process.pid)
    response.headers.add('Access-Control-Allow-Origin', '*')
    return response

@app.route("/queue/<id>", methods=["GET"])
def getStatus(id):
    process = QUEUE.get(id, None)
    if process is None:
        abort(404, description="Process not found")
    retcode = process.poll()
    if retcode is None:
        content = {'download_url': None, 'message': 'Process is still running.'}
    else:
        # QUEUE.pop(id) # Remove reference from QUEUE ?
        content = {'download_url': 'download.com', 'message': f'process has completed with retcode: {retcode}'}
    response = jsonify(content)
    response.headers.add('Access-Control-Allow-Origin', '*')
    return response

if __name__ == '__main__':
    app.run(host='0.0.0.0',port=8080)

如果此应用程序不仅仅用作单个项目,您还必须考虑其他因素。

  • 我们使用QUEUE全局变量来存储进程的状态。但在实际项目中,通过 wsgi / gunicorn 的部署可以有多个 worker,每个 worker 都有自己的全局变量。因此,为了扩大规模,请考虑改用 redis / mq 数据存储。

  • 队列是否需要清理?该不该清理?它有一个缺点,如果你在值被 GET 一次后清理它,下一个 GET 获取 404。如果 GET api 必须是幂等的(很可能是),这是一个设计决定。