【问题标题】:psutil's cpu_percent always returns 0.0psutil 的 cpu_percent 总是返回 0.0
【发布时间】:2016-11-27 00:38:09
【问题描述】:

我希望我的 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 将始终为 cpu_percent 返回 0.0:

$ 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()

这会正确返回大于 0.0 的值。

【问题讨论】:

  • 这是一种自我参照;应用程序将消耗额外的周期和内存,只是为了报告它已经消耗了多少周期和内存。这可以通过其他流程监控应用程序和服务来实现;你为什么要在你的应用中使用这个?

标签: flask monitoring metrics psutil


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

    正如 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,然后将模块导入您的 main 并启动监控循环。

    import monitor
    monitor.start_monitor()
    

    然后在每次请求调用时:

    monitor.cpu_averages()
    

    并提取您认为有意义的时间段的价值。

    【讨论】:

      【解决方案3】:

      格雷厄姆的解决方案似乎有效,但是我找到了一种更简单的解决方案,通过告诉它间隔,在这个例子中它测量最后一秒:

      psutil.cpu_percent(interval=1)
      

      【讨论】:

        猜你喜欢
        • 2015-07-06
        • 2018-07-16
        • 2018-05-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多