psutil 的 cpu_percent 总是 returns 0.0

psutil's cpu_percent always returns 0.0

我希望我的 Flask 应用程序以百分比形式报告当前使用的 CPU 和内存量:

import psutil
from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route("/test", methods=["GET"])
def healthz():
    return jsonify(msg="OK"), 200

@app.route("/stats", methods=["GET"])
def stats():
    p = psutil.Process()
    json_body = {
        "cpu_percent": p.cpu_percent(interval=None),
        "cpu_times": p.cpu_times(),
        "mem_info": p.memory_info(),
        "mem_percent": p.memory_percent()
    }
    return jsonify(json_body), 200


def main():
    app.run(host="0.0.0.0", port=8000, debug=False)

if __name__ == '__main__':
    main()

在向 /test 发送大量请求时,/stats 将始终 returns 0.0 cpu_percent:

$ while true; do curl http://127.0.0.1:8000/test &>/dev/null; done &
$ curl http://127.0.0.1:8000/stats
{
  "cpu_percent": 0.0, 
  "cpu_times": [
    4.97, 
    1.28, 
    0.0, 
    0.0
  ], 
  "mem_info": [
    19652608, 
    243068928, 
    4292608, 
    4096, 
    0, 
    14675968, 
    0
  ], 
  "mem_percent": 1.8873787935409003
}

但是,如果我使用 ipython 手动检查:

import psutil
p = psutil.Process(10993)
p.cpu_percent()

这正确 returns 一个大于 0.0 的值。

只需全局定义 "p = psutil.Process()"(在 stat() 函数之外)。 cpu_percent() 自上次调用以来跟踪 CPU 次,这就是它能够确定百分比的方式。

第一次调用将始终为 0.0,因为计算百分比需要随时间比较两个值,因此,必须经过一些时间。

正如 Giampaolo 指出的那样,Process 的实例需要在全局范围内,因为该实例会跟踪状态以根据先前的调用解决问题。

请注意,CPU 百分比从一个时刻到另一个时刻可能会跳跃很多,尤其是在计算它的时间段不断变化的情况下,这可能会非常混乱。使用后台线程可能会更好,它可以计算出设定时间范围内的 CPU 百分比平均值。

我手边的一些代码可能对你有用:

from __future__ import print_function

import os
import time
import atexit

import threading

try:
    import Queue as queue
except ImportError:
    import queue

import psutil

_running = False
_queue = queue.Queue()
_lock = threading.Lock()

_cpu_percentage = 1800 * [0.0]
_processes = {}

def _monitor():
    global _cpu_percentage
    global _processes

    while True:
        marker = time.time()

        total = 0.0
        pids = psutil.pids()

        processes = {}

        for pid in pids:
            process = _processes.get(pid)
            if process is None:
                process = psutil.Process(pid)
            processes[pid] = process
            total += process.cpu_percent()

        _processes = processes

        _cpu_percentage.insert(0, total)

        _cpu_percentage = _cpu_percentage[:1800]

        duration = max(0.0, 1.0 - (time.time() - marker))

        try:
            return _queue.get(timeout=duration)

        except queue.Empty:
            pass

_thread = threading.Thread(target=_monitor)
_thread.setDaemon(True)

def _exiting():
    try:
        _queue.put(True)
    except Exception:
        pass
    _thread.join()

def track_changes(path):
    if not path in _files:
        _files.append(path)

def start_monitor():
    global _running
    _lock.acquire()
    if not _running:
        prefix = 'monitor (pid=%d):' % os.getpid()
        print('%s Starting CPU monitor.' % prefix)
        _running = True
        _thread.start()
        atexit.register(_exiting)
    _lock.release()

def cpu_averages():
    values = _cpu_percentage[:60]

    averages = {}

    def average(secs):
        return min(100.0, sum(values[:secs])/secs)

    averages['cpu.average.1s'] = average(1)
    averages['cpu.average.5s'] = average(5)
    averages['cpu.average.15s'] = average(15)
    averages['cpu.average.30s'] = average(30)
    averages['cpu.average.1m'] = average(60)
    averages['cpu.average.5m'] = average(300)
    averages['cpu.average.15m'] = average(900)
    averages['cpu.average.30m'] = average(1800)

    return averages

我删除了其中的其他内容,希望剩下的内容仍处于可用状态。

要使用它,请将其添加到文件 monitor.py,然后将模块导入您的主程序并启动监控循环。

import monitor
monitor.start_monitor()

然后在每次请求时调用:

monitor.cpu_averages()

并提取您认为合理的时间段的价值。

Graham 的解决方案似乎可行,但我找到了一种更简单的解决方案,通过告诉它间隔,在这个例子中它测量最后一秒:

psutil.cpu_percent(interval=1)