【运维】掌控全局:用 Python 和 psutil 全方位透视你的系统性能

《Python OpenCV从菜鸟到高手》带你进入图像处理与计算机视觉的大门!

解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界

系统管理员和开发者经常需要监控服务器或个人电脑的性能,以便及时发现瓶颈、优化资源利用率,甚至预测潜在问题。Python 的 psutil 库提供了一个强大、跨平台的方式来获取各种系统指标,如 CPU 使用率、内存占用、磁盘 I/O、网络流量等。本文将深入探讨 psutil 的功能,结合大量代码示例(包括详细的中文注释)和 LaTeX 数学公式,展示如何利用它构建全面的系统监控工具。您将学会如何收集、处理和展示性能数据,最终实现对系统健康状况的全面掌控。本文的目标是让您能够构建自己的定制化监控解决方案,或将 psutil 集成到现有的运维工具链中。

1. 引言:为什么我们需要系统监控?

在数字化时代,无论是个人电脑还是大型服务器集群,系统的稳定性和性能都至关重要。系统监控就像是给我们的数字基础设施安装了一个“健康检查仪”,可以帮助我们:

及时发现问题: 在系统崩溃或性能严重下降之前,预警潜在的硬件故障、资源耗尽等问题。
优化性能: 识别系统瓶颈,例如 CPU 过载、内存不足、磁盘 I/O 阻塞等,从而进行针对性的优化。
资源规划: 了解系统资源的使用趋势,预测未来的需求,为扩容或缩容提供依据。
故障排除: 当问题发生时,提供详细的性能数据,帮助快速定位问题的根源。
安全审计: 监控异常的网络流量、进程活动等,有助于发现潜在的安全威胁。

psutil (process and system utilities) 是一个跨平台的 Python 库,可以轻松获取系统和进程的各种信息,是实现系统监控的理想工具。

2. psutil 库:安装与初步探索

2.1 安装

安装 psutil 非常简单,使用 pip 即可:

pip install psutil
2.2 初步探索:获取 CPU 信息

让我们从获取 CPU 信息开始,体验 psutil 的便捷性:

# 导入 psutil 库
import psutil

# 获取 CPU 逻辑核心数
cpu_count_logical = psutil.cpu_count()
print(f"CPU 逻辑核心数: {
              cpu_count_logical}")

# 获取 CPU 物理核心数
cpu_count_physical = psutil.cpu_count(logical=False)
print(f"CPU 物理核心数: {
              cpu_count_physical}")

# 获取 CPU 使用率(每隔 1 秒刷新一次)
for i in range(5):  # 监控5次
    cpu_percent = psutil.cpu_percent(interval=1)
    print(f"CPU 使用率: {
              cpu_percent}%")

这段代码展示了如何获取 CPU 的逻辑核心数、物理核心数,以及每秒的 CPU 使用率。psutil.cpu_percent() 函数的 interval 参数指定了采样间隔。

3. CPU 监控:深入挖掘

3.1 CPU 详细使用率

除了总体 CPU 使用率,psutil 还可以提供更详细的信息:

# 获取 CPU 的详细使用率信息
cpu_times_percent = psutil.cpu_times_percent(interval=1)

print(f"用户态 CPU 使用率: {
              cpu_times_percent.user}%")
print(f"内核态 CPU 使用率: {
              cpu_times_percent.system}%")
print(f"空闲 CPU 使用率: {
              cpu_times_percent.idle}%")
print(f"I/O 等待 CPU 使用率: {
              cpu_times_percent.iowait}%")  # 注意:某些系统可能不支持
# ... 其他指标,如 nice, irq, softirq, steal, guest, guest_nice

cpu_times_percent() 返回一个命名元组,包含了 CPU 在不同模式下花费时间的百分比。这些指标对于分析 CPU 负载的类型非常有用。例如,如果 iowait 很高,可能表示磁盘 I/O 存在瓶颈。

3.2 CPU 负载:平均负载

除了瞬时使用率,我们还可以获取系统的平均负载,这是一个反映系统繁忙程度的指标:

# 获取系统平均负载(1 分钟、5 分钟、15 分钟)
load_avg = psutil.getloadavg()
print(f"1 分钟平均负载: {
              load_avg[0]}")
print(f"5 分钟平均负载: {
              load_avg[1]}")
print(f"15 分钟平均负载: {
              load_avg[2]}")

# 建议:将平均负载除以 CPU 核心数,得到一个更直观的指标
load_avg_per_core = [x / psutil.cpu_count() for x in load_avg]
print(f"每核心 1 分钟平均负载: {
              load_avg_per_core[0]:.2f}")
print(f"每核心 5 分钟平均负载: {
              load_avg_per_core[1]:.2f}")
print(f"每核心 15 分钟平均负载: {
              load_avg_per_core[2]:.2f}")

平均负载表示在一段时间内,系统中处于可运行状态(正在使用 CPU 或等待 CPU)的平均进程数。一般来说,如果每核心的平均负载持续高于 0.7,就可能需要关注系统的性能问题。

3.3 CPU 频率
# 获取当前 CPU 频率
cpu_freq = psutil.cpu_freq()
print(f"当前 CPU 频率: {
              cpu_freq.current} MHz")
print(f"最小 CPU 频率: {
              cpu_freq.min} MHz")
print(f"最大 CPU 频率: {
              cpu_freq.max} MHz")

cpu_freq() 返回一个命名元组,其中包含了当前频率、最小频率和最大频率。

3.4 CPU 温度 (部分硬件支持)
# 获取 CPU 温度(如果硬件支持)
try:
    cpu_temperatures = psutil.sensors_temperatures()
    if 'coretemp' in cpu_temperatures: #不同平台,key值可能不同
        for entry in cpu_temperatures['coretemp']:
            print(f"{
              entry.label}: {
              entry.current}°C")
except AttributeError:
    print("无法获取 CPU 温度信息。")

部分硬件和操作系统可能不支持该功能。

4. 内存监控

4.1 内存使用情况
# 获取内存使用情况
memory = psutil.virtual_memory()

print(f"总内存: {
              memory.total / (1024 ** 3):.2f} GB")  # 转换为 GB
print(f"已使用内存: {
              memory.used / (1024 ** 3):.2f} GB")
print(f"可用内存: {
              memory.available / (1024 ** 3):.2f} GB")
print(f"内存使用率: {
              memory.percent}%")
print(f"空闲内存: {
              memory.free/ (1024 ** 3):.2f} GB")
# ... 其他指标,如 active, inactive, buffers, cached, shared, slab

psutil.virtual_memory() 返回一个命名元组,包含了各种内存指标。total 是总内存,used 是已使用的内存,available 是可供应用程序使用的内存(包括空闲内存和可回收的缓存),percent 是内存使用率。

4.2 Swap 交换分区
# 获取 Swap 交换分区使用情况
swap = psutil.swap_memory()

print(f"总 Swap 空间: {
              swap.total / (1024 ** 3):.2f} GB")
print(f"已使用 Swap 空间: {
              swap.used / (1024 ** 3):.2f} GB")
print(f"空闲 Swap 空间: {
              swap.free / (1024 ** 3):.2f} GB")
print(f"Swap 使用率: {
              swap.percent}%")
print(f"从磁盘换入内存: {
              swap.sin / (1024 ** 3):.2f} GB") #数据量
print(f"从内存换出到磁盘: {
              swap.sout / (1024 ** 3):.2f} GB")#数据量

Swap 交换分区是在物理内存不足时,将一部分硬盘空间用作内存的区域。swap_memory() 提供了 Swap 分区的总大小、已用大小、空闲大小、使用率等信息。sinsout 分别表示从磁盘换入内存和从内存换出到磁盘的数据量。

5. 磁盘监控

5.1 磁盘分区信息
# 获取磁盘分区信息
partitions = psutil.disk_partitions()

for partition in partitions:
    print(f"设备: {
              partition.device}")
    print(f"挂载点: {
              partition.mountpoint}")
    print(f"文件系统类型: {
              partition.fstype}")
    try:
        usage = psutil.disk_usage(partition.mountpoint)
        print(f"  总空间: {
              usage.total / (1024 ** 3):.2f} GB")
        print(f"  已用空间: {
              usage.used / (1024 ** 3):.2f} GB")
        print(f"  空闲空间: {
              usage.free / (1024 ** 3):.2f} GB")
        print(f"  使用率: {
              usage.percent}%")
    except PermissionError:
        print("  无权限访问该分区信息。")

这段代码会列出所有磁盘分区及其详细信息,包括设备名、挂载点、文件系统类型、总空间、已用空间、空闲空间和使用率。

5.2 磁盘 I/O 统计
# 获取磁盘 I/O 统计信息
disk_io = psutil.disk_io_counters()

print(f"读取次数: {
              disk_io.read_count}")
print(f"写入次数: {
              disk_io.write_count}")
print(f"读取字节数: {
              disk_io.read_bytes / (1024 ** 3):.2f} GB")
print(f"写入字节数: {
              disk_io.write_bytes / (1024 ** 3):.2f} GB")
print(f"读取时间: {
              disk_io.read_time} ms")
print(f"写入时间: {
              disk_io.write_time} ms")

# 可以针对每个磁盘单独获取 I/O 统计
for disk, io in psutil.disk_io_counters(perdisk=True).items():
    print(f"磁盘 {
              disk}:")
    print(f"  读取次数: {
              io.read_count}")
    print(f"  写入次数: {
              io.write_count}")
    # ... 其他指标

psutil.disk_io_counters() 返回磁盘的 I/O 统计信息,包括读写次数、读写字节数、读写时间等。通过 perdisk=True 参数,可以获取每个磁盘的独立 I/O 统计。

6. 网络监控

6.1 网络接口信息
# 获取网络接口信息
net_io = psutil.net_io_counters(pernic=True)

for interface, io in net_io.items():
    print(f"接口 {
              interface}:")
    print(f"  发送字节数: {
              io.bytes_sent / (1024 ** 2):.2f} MB")
    print(f"  接收字节数: {
              io.bytes_recv / (1024 ** 2):.2f} MB")
    print(f"  发送数据包数: {
              io.packets_sent}")
    print(f"  接收数据包数: {
              io.packets_recv}")
    print(f"  接收错误数: {
              io.errin}")
    print(f"  发送错误数: {
              io.errout}")
    print(f"  接收丢包数: {
              io.dropin}")
    print(f"  发送丢包数: {
              io.dropout}")

# 获取网络连接信息
connections = psutil.net_connections()
for conn in connections:
     print(conn)

这段代码展示了如何获取每个网络接口的 I/O 统计信息,包括发送和接收的字节数、数据包数、错误数、丢包数等。 还可以获取当前的上网络连接

6.2 网卡状态
import psutil

addrs = psutil.net_if_addrs()
for interface_name, interface_addresses in addrs.items():
    print(f"接口: {
              interface_name}")
    for address in interface_addresses:
        print(f"  Address Family: {
              address.family}")
        print(f"  Address: {
              address.address}")
        print(f"  Netmask: {
              address.netmask}")
        print(f"  Broadcast Address: {
              address.broadcast}")
        print(f"  PTP Destination: {
              address.ptp}")

这段代码可以枚举出网卡的详细状态。

7. 进程监控

7.1 获取进程列表
# 获取所有进程的 PID 列表
pids = psutil.pids()

for pid in pids:
    try:
        p = psutil.Process(pid)
        print(f"PID: {
              pid}, 进程名: {
              p.name()}, 状态: {
              p.status()}, 命令行: {
              p.cmdline()}")
    except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
        pass  # 忽略可能出现的异常

psutil.pids() 返回所有进程的 PID 列表。psutil.Process() 可以根据 PID 获取进程对象,进而获取进程的各种信息,如进程名、状态、命令行参数等。

7.2 进程详细信息
# 获取指定 PID 的进程信息
pid_to_monitor = 1234  # 替换为你要监控的 PID

try:
    p = psutil.Process(pid_to_monitor)

    print(f"进程名: {
              p.name()}")
    print(f"命令行: {
              p.cmdline()}")
    print(f"状态: {
              p.status()}")
    print(f"父进程 PID: {
              p.ppid()}")
    print(f"CPU 使用率: {
              p.cpu_percent()}%")
    print(f"内存使用率: {
              p.memory_percent()}%")
    print(f"打开的文件数: {
              p.num_fds()}")  # Linux/macOS
    print(f"线程数: {
              p.num_threads()}")
    print(f"创建时间: {
              p.create_time()}") #unix时间戳

    # 获取进程打开的文件列表
    open_files = p.open_files()
    for file in open_files:
        print(f"  打开的文件: {
              file.path}")

    # 获取进程的网络连接
    connections = p.connections()
    for conn in connections:
        print(f"  本地地址: {
              conn.laddr}, 远程地址: {
              conn.raddr}, 状态: {
              conn.status}")

    # 获取进程的内存映射信息
    # memory_maps = p.memory_maps()  # 可能会消耗大量资源
    # for mm in memory_maps:
    #     print(f"    路径: {mm.path}, 大小: {mm.rss / (1024 ** 2):.2f} MB")

except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
    print(f"无法获取 PID 为 {
              pid_to_monitor} 的进程信息。")

这段代码展示了如何获取一个指定 PID 进程的各种详细信息,包括进程名、命令行、状态、父进程 PID、CPU 使用率、内存使用率、打开的文件数、线程数、创建时间、打开的文件列表、网络连接等。

7.3 进程树
# 递归打印进程树
def print_process_tree(pid, indent=""):
    try:
        p = psutil.Process(pid)
        print(f"{
              indent}PID: {
              pid}, 进程名: {
              p.name()}")
        for child in p.children():
            print_process_tree(child.pid, indent + "  ")
    except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
        pass

# 从根进程(PID 1)开始打印进程树
print_process_tree(1)

这段代码实现了一个递归函数 print_process_tree,用于打印进程树。它从指定的 PID 开始,递归地获取子进程并打印信息。

8. 其他系统信息

8.1 系统启动时间
# 获取系统启动时间
boot_time = psutil.boot_time()
print(f"系统启动时间: {
              boot_time}") #unix时间戳
import datetime
print(f"系统启动时间: {
              datetime.datetime.fromtimestamp(boot_time).strftime('%Y-%m-%d %H:%M:%S')}")

# 获取当前登录用户信息
users = psutil.users()

for user in users:
    print(f"用户名: {
              user.name}")
    print(f"终端: {
              user.terminal}")
    print(f"登录时间: {
              datetime.datetime.fromtimestamp(user.started).strftime('%Y-%m-%d %H:%M:%S')}")
    print(f"主机: {
              user.host}")

psutil.users() 返回一个列表,包含了当前登录用户的信息,如用户名、终端、登录时间、主机等。

8.3 传感器信息(温度、风扇、电池)
# 获取传感器信息(温度、风扇转速)
try:
    temperatures = psutil.sensors_temperatures()
    print("温度信息:")
    for name, entries in temperatures.items():
        print(f"  {
              name}:")
        for entry in entries:
            print(f"    {
              entry.label}: {
              entry.current}°C")

    fans = psutil.sensors_fans()
    print("
风扇信息:")
    for name, entries in fans.items():
        print(f"  {
              name}:")
        for entry in entries:
            print(f"    {
              entry.label}: {
              entry.current} RPM")
except AttributeError:
    print("无法获取温度或风扇信息。")

# 获取电池信息(如果可用)
try:
    battery = psutil.sensors_battery()
    if battery:
        print("
电池信息:")
        print(f"  剩余电量: {
              battery.percent}%")
        print(f"  剩余时间: {
              battery.secsleft / 3600:.2f} 小时")  # 转换为小时
        print(f"  电源状态: {
              '已连接' if battery.power_plugged else '未连接'}")
except AttributeError:
    print("无法获取电池信息。")

psutil.sensors_temperatures()psutil.sensors_fans()psutil.sensors_battery() 分别用于获取温度、风扇和电池信息。请注意,这些功能的支持程度取决于硬件和操作系统。

9. 构建实用的监控工具

有了 psutil 提供的丰富功能,我们可以构建各种实用的监控工具。下面是一些示例:

9.1 简单的命令行监控工具
import time
import os

def clear_screen():
    os.system('cls' if os.name == 'nt' else 'clear')

def monitor():
    while True:
        clear_screen()
        print("=" * 40)
        print("系统监控 (按 Ctrl+C 退出)")
        print("=" * 40)

        # CPU 信息
        print(f"CPU 使用率: {
              psutil.cpu_percent()}%")
        load_avg = [x / psutil.cpu_count() for x in psutil.getloadavg()]
        print(f"平均负载 (1/5/15 分钟): {
              load_avg[0]:.2f}, {
              load_avg[1]:.2f}, {
              load_avg[2]:.2f}")

        # 内存信息
        memory = psutil.virtual_memory()
        print(f"内存使用率: {
              memory.percent}%")
        print(f"已用内存: {
              memory.used / (1024 ** 3):.2f} GB / {
              memory.total / (1024 ** 3):.2f} GB")

        # 磁盘信息(仅显示根分区)
        disk_usage = psutil.disk_usage('/')
        print(f"磁盘使用率: {
              disk_usage.percent}%")
        print(f"已用磁盘空间: {
              disk_usage.used / (1024 ** 3):.2f} GB / {
              disk_usage.total / (1024 ** 3):.2f} GB")

        # 网络信息(仅显示总流量)
        net_io = psutil.net_io_counters()
        print(f"网络发送: {
              net_io.bytes_sent / (1024 ** 2):.2f} MB, 接收: {
              net_io.bytes_recv / (1024 ** 2):.2f} MB")

        time.sleep(2)  # 每 2 秒刷新一次

if __name__ == "__main__":
    try:
        monitor()
    except KeyboardInterrupt:
        print("
监控已停止。")

这个简单的脚本会在命令行中实时显示 CPU 使用率、平均负载、内存使用情况、磁盘使用情况和网络流量。每 2 秒刷新一次,按 Ctrl+C 可以退出。

9.2 生成 HTML 报告
import jinja2  # 需要安装 jinja2:pip install jinja2

def generate_html_report():
    # 收集数据
    cpu_percent = psutil.cpu_percent()
    load_avg = [x / psutil.cpu_count() for x in psutil.getloadavg()]
    memory = psutil.virtual_memory()
    disk_usage = psutil.disk_usage('/')
    net_io = psutil.net_io_counters()

    # 加载 Jinja2 模板
    template_loader = jinja2.FileSystemLoader(searchpath="./")
    template_env = jinja2.Environment(loader=template_loader)
    template_file = "report_template.html"  # 模板文件名
    template = template_env.get_template(template_file)

    # 渲染模板
    output_text = template.render(
        cpu_percent=cpu_percent,
        load_avg=load_avg,
        memory=memory,
        disk_usage=disk_usage,
        net_io=net_io,
        datetime=datetime
    )

    # 将报告写入 HTML 文件
    with open("system_report.html", "w") as f:
        f.write(output_text)

    print("已生成系统报告: system_report.html")

# 创建 report_template.html 文件,内容如下:
"""
<!DOCTYPE html>
<html>
<head>
    <title>系统性能报告</title>
</head>
<body>
    <h1>系统性能报告</h1>
    <p>生成时间: {
            { datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') }}</p>

    <h2>CPU</h2>
    <p>CPU 使用率: {
            { cpu_percent }}%</p>
    <p>平均负载 (1/5/15 分钟): {
            { load_avg[0]:.2f }}, {
            { load_avg[1]:.2f }}, {
            { load_avg[2]:.2f }}</p>

    <h2>内存</h2>
    <p>内存使用率: {
            { memory.percent }}%</p>
    <p>已用内存: {
            { memory.used / (1024 ** 3):.2f }} GB / {
            { memory.total / (1024 ** 3):.2f }} GB</p>

    <h2>磁盘 (/)</h2>
    <p>磁盘使用率: {
            { disk_usage.percent }}%</p>
    <p>已用磁盘空间: {
            { disk_usage.used / (1024 ** 3):.2f }} GB / {
            { disk_usage.total / (1024 ** 3):.2f }} GB</p>

    <h2>网络</h2>
    <p>网络发送: {
            { net_io.bytes_sent / (1024 ** 2):.2f }} MB, 接收: {
            { net_io.bytes_recv / (1024 ** 2):.2f }} MB</p>
</body>
</html>
"""

if __name__ == "__main__":
    generate_html_report()

这个例子使用 Jinja2 模板引擎生成一个 HTML 格式的系统报告。你需要先创建一个名为 report_template.html 的模板文件,然后在 Python 脚本中收集数据、加载模板、渲染模板,最后将结果写入 HTML 文件。

9.3 使用 Matplotlib 绘制图表 (进阶)
import matplotlib.pyplot as plt
# 如果没有安装matplotlib, 需要安装: pip install matplotlib

def plot_cpu_usage(history):
    plt.figure(figsize=(10, 6))
    plt.plot(history)
    plt.xlabel("Time (seconds)")
    plt.ylabel("CPU Usage (%)")
    plt.title("CPU Usage Over Time")
    plt.grid(True)
    plt.ylim(0, 100)  # 设置 y 轴范围
    plt.show()

def plot_memory_usage(history):
    plt.figure(figsize=(10,6))
    plt.plot(history)
    plt.xlabel("Time (seconds)")
    plt.ylabel("Memory Usage (%)")
    plt.title("Memory Usage Over Time")
    plt.grid(True)
    plt.ylim(0,100)
    plt.show()

def monitor_and_plot():
    cpu_history = []
    memory_history = []
    try:
        while True:
            cpu_history.append(psutil.cpu_percent())
            memory_history.append(psutil.virtual_memory().percent)
            time.sleep(1)
            if len(cpu_history) > 60: #保留60个点(1分钟数据)
                cpu_history.pop(0)
            if len(memory_history) > 60:
                memory_history.pop(0)


    except KeyboardInterrupt:
        print("
监控已停止,正在生成图表...")
        plot_cpu_usage(cpu_history)
        plot_memory_usage(memory_history)

if __name__ == "__main__":
     monitor_and_plot()

这个示例使用 Matplotlib 库绘制 CPU 使用率和内存使用率的实时图表。它会持续监控 CPU 和内存,并将数据保存在历史列表中,然后在用户按下 Ctrl+C 时绘制图表。

10. 高级应用与进阶

10.1 与数据库集成

可以将 psutil 收集的数据存储到数据库中,例如 SQLite、MySQL、PostgreSQL 或时间序列数据库(如 InfluxDB、Prometheus),以便进行长期存储、分析和可视化。

import sqlite3
import time

def create_database():
    conn = sqlite3.connect('system_metrics.db')
    cursor = conn.cursor()
    cursor.execute('''
        CREATE TABLE IF NOT EXISTS cpu_metrics (
            timestamp INTEGER,
            cpu_percent REAL
        )
    ''')
     cursor.execute('''
        CREATE TABLE IF NOT EXISTS memory_metrics (
            timestamp INTEGER,
            memory_percent REAL
        )
    ''')
    conn.commit()
    conn.close()

def insert_data(cpu_percent, memory_percent):
    conn = sqlite3.connect('system_metrics.db')
    cursor = conn.cursor()
    timestamp = int(time.time())
    cursor.execute("INSERT INTO cpu_metrics (timestamp, cpu_percent) VALUES (?, ?)", (timestamp, cpu_percent))
    cursor.execute("INSERT INTO memory_metrics (timestamp, memory_percent) VALUES(?,?)", (timestamp, memory_percent))
    conn.commit()
    conn.close()

def monitor_and_store():
    create_database()
    while True:
        cpu_percent = psutil.cpu_percent()
        memory_percent = psutil.virtual_memory().percent
        insert_data(cpu_percent, memory_percent)
        time.sleep(60)  # 每分钟记录一次

if __name__ == "__main__":
    try:
        monitor_and_store()
    except KeyboardInterrupt:
        print("
监控已停止。")

这个例子展示了如何将 CPU 和内存使用率数据存储到 SQLite 数据库中。您可以根据需要修改代码以适应其他数据库。

10.2 与监控系统集成

可以将 psutil 集成到现有的监控系统中,例如 Zabbix、Nagios、Grafana 等。通常,这需要编写自定义脚本或插件,将 psutil 收集的数据发送到监控系统。

以Zabbix为例,可以创建一个自定义的Zabbix agent脚本:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# zabbix_psutil.py (需要放在Zabbix Agent的UserParameter配置目录中)
# 在 zabbix_agentd.conf 中配置:
# UserParameter=system.cpu.percent,python3 /path/to/zabbix_psutil.py cpu_percent
# UserParameter=system.memory.percent,python3 /path/to/zabbix_psutil.py memory_percent

import psutil
import sys

def get_cpu_percent():
    return psutil.cpu_percent()

def get_memory_percent():
    return psutil.virtual_memory().percent

if __name__ == "__main__":
    if len(sys.argv) != 2:
        print("Usage: python zabbix_psutil.py <metric_name>")
        sys.exit(1)

    metric_name = sys.argv[1]

    if metric_name == "cpu_percent":
        print(get_cpu_percent())
    elif metric_name == "memory_percent":
        print(get_memory_percent())
    else:
        print("Invalid metric name.")
        sys.exit(1)
10.3 告警和通知

可以基于 psutil 收集的数据设置告警规则,并在触发告警时发送通知。例如,当 CPU 使用率超过 90% 时,发送电子邮件或短信通知。

import smtplib
from email.mime.text import MIMEText

def send_email(subject, body, sender, recipients, password):
    msg = MIMEText(body)
    msg['Subject'] = subject
    msg['From'] = sender
    msg['To'] = ', '.join(recipients)
    with smtplib.SMTP_SSL('smtp.gmail.com', 465) as smtp_server: #以gmail为例,需要设置允许不太安全的应用访问
       smtp_server.login(sender, password)
       smtp_server.sendmail(sender, recipients, msg.as_string())
    print("邮件发送成功!")


def monitor_and_alert():
    cpu_threshold = 90
    memory_threshold = 90
    sender_email = "your_email@gmail.com"  # 发件人邮箱
    sender_password = "your_password"  # 发件人邮箱密码/授权码
    recipient_emails = ["recipient1@example.com", "recipient2@example.com"]  # 收件人邮箱列表

    while True:
        cpu_percent = psutil.cpu_percent()
        memory_percent = psutil.virtual_memory().percent

        if cpu_percent > cpu_threshold:
            subject = "CPU 使用率过高警告!"
            body = f"当前 CPU 使用率: {
              cpu_percent}%"
            send_email(subject, body, sender_email, recipient_emails, sender_password)

        if memory_percent > memory_threshold:
            subject = "内存使用率过高警告!"
            body = f"当前内存使用率: {
              memory_percent}%"
            send_email(subject, body, sender_email, recipient_emails, sender_password)

        time.sleep(60)

if __name__ == "__main__":
    try:
        monitor_and_alert()
    except KeyboardInterrupt:
     print("监控停止")
    except Exception as e:
        print(f"发生错误:{
              e}")

这个示例演示了如何在 CPU 或内存使用率超过阈值时发送电子邮件通知。您可以使用类似的方法发送短信、Slack 消息等。

11. 性能优化与注意事项

采样间隔: psutil 的一些函数(如 cpu_percent())需要指定采样间隔。过短的间隔会增加 CPU 开销,过长的间隔则可能错过短暂的峰值。需要根据实际需求进行权衡。
资源消耗: 频繁地获取大量系统信息可能会对系统性能产生一定影响。在生产环境中,应谨慎使用 psutil,避免过度监控。
异常处理: 在访问进程信息时,可能会遇到 NoSuchProcessAccessDeniedZombieProcess 等异常。务必进行适当的异常处理。
跨平台兼容性: 虽然 psutil 是跨平台的,但某些功能的可用性可能因操作系统而异。在编写代码时,要注意处理可能出现的差异。
权限问题: 部分操作可能需要管理员权限。

12. 总结

psutil 是一个功能强大的 Python 库,为系统监控提供了丰富而便捷的接口。通过本文的学习,您应该已经掌握了如何使用 psutil 获取 CPU、内存、磁盘、网络和进程等各种系统信息,并能够构建简单的监控工具、生成报告、与数据库集成、设置告警等。

psutil 的应用远不止于此。您可以将其与其他工具和技术结合,构建更复杂的监控系统、自动化运维工具、性能分析工具等。掌握 psutil,将为您的系统管理和开发工作带来极大的便利。

数学公式示例 (LaTeX)

虽然本文主要关注 psutil 的使用,但为了完整性,这里提供一些可能与系统监控相关的数学公式示例:

指数加权移动平均 (Exponentially Weighted Moving Average, EWMA):

EWMA 是一种常用的平滑数据的方法,可以用于处理 CPU 使用率、内存使用率等时间序列数据,以减少噪声并突出趋势。

S t = { Y t , t = 0 α Y t + ( 1 − α ) S t − 1 , t > 0 S_t = egin{cases} Y_t, & t = 0 \ alpha Y_t + (1 – alpha)S_{t-1}, & t > 0 end{cases} St​={
Yt​,αYt​+(1−α)St−1​,​t=0t>0​其中:

S _ t S\_t S_t 是在时间 t 的 EWMA 值。
Y _ t Y\_t Y_t 是在时间 t 的实际观测值。
a l p h a \alpha alpha 是平滑因子,取值范围为 (0, 1)。 a l p h a \alpha alpha 越大,最近的观测值权重越大,对变化的响应越快; a l p h a \alpha alpha 越小,越多的历史数据被考虑,曲线越平滑。

在 Python 中,可以使用 pandas 库来计算 EWMA:

import pandas as pd
import numpy as np
import psutil
import time
# 如果没有安装pandas, 需要安装: pip install pandas

def get_cpu_usage_series(interval=1, num_points=60):
    """获取 CPU 使用率时间序列"""
    cpu_usage = []
    for _ in range(num_points):
        cpu_usage.append(psutil.cpu_percent(interval=interval))
    return pd.Series(cpu_usage)

def calculate_ewma(series, alpha=0.1):
    """计算指数加权移动平均"""
    return series.ewm(alpha=alpha).mean()

if __name__ == "__main__":
    cpu_series = get_cpu_usage_series()
    ewma_series = calculate_ewma(cpu_series)

    print("原始 CPU 使用率:", cpu_series.values)
    print("EWMA 后的 CPU 使用率:", ewma_series.values)

    # 可以结合 Matplotlib 绘图
    import matplotlib.pyplot as plt

    plt.figure(figsize=(12, 6))
    plt.plot(cpu_series, label="Original")
    plt.plot(ewma_series, label="EWMA (alpha=0.1)")
    plt.xlabel("Time")
    plt.ylabel("CPU Usage (%)")
    plt.legend()
    plt.title("CPU Usage with EWMA")
    plt.show()

标准差 (Standard Deviation):

标准差用于衡量数据的离散程度。在系统监控中,可以用来衡量 CPU 使用率、内存使用率等指标的波动性。

σ = 1 N ∑ i = 1 N ( x i − μ ) 2 sigma = sqrt{frac{1}{N}sum_{i=1}^{N}(x_i – mu)^2} σ=N1​i=1∑N​(xi​−μ)2
​其中:

s i g m a \sigma sigma 是标准差。
N N N 是数据点的数量。
x _ i x\_i x_i 是第 i 个数据点的值。
m u \mu mu 是数据的平均值。

在 Python 中,可以使用 numpy 来计算标准差:

```python
import numpy as np
cpu_usage = [10, 12, 15, 13, 18, 20, 17, 15, 14, 16] # 示例数据
std_dev = np.std(cpu_usage)
print(f"CPU使用率的标准差: {std_dev:.2f}")
```

百分位数 (Percentiles)

百分位数用于表示数据集中特定百分比的数据点所在的位置。在监控领域,常用的有 P95, P99(95%, 99%)等。

计算方法:将数据从小到大排序,P95 表示有 95% 的数据点小于或等于该值。

import numpy as np
cpu_usage = [10, 12, 15, 13, 18, 20, 17, 15, 14, 16, 80, 95] # 示例数据(有异常值)
p95 = np.percentile(cpu_usage, 95)
print(f"CPU 使用率的 P95: {
                p95}")

相关系数 (Correlation Coefficient)

相关系数用来衡量两个变量之间的线性相关程度。例如,可以用它来分析 CPU 使用率和内存使用率之间的关系。

皮尔逊相关系数 (Pearson Correlation Coefficient) 公式:

r = frac{sum_{i=1}^{n}(X_i – ar{X})(Y_i – ar{Y})}{sqrt{sum_{i=1}^{n}(X_i – ar{X})2}sqrt{sum_{i=1}{n}(Y_i – ar{Y})^2}}

其中:

r r r 是皮尔逊相关系数。
X _ i X\_i X_i 和 Y _ i Y\_i Y_i 是两个变量的第 i 个观测值。
b a r X \bar{X} barX 和 b a r Y \bar{Y} barY 分别是两个变量的平均值。

在 Python 中,可以使用 numpyscipy 来计算相关系数:

import numpy as np
from scipy.stats import pearsonr

# 示例数据
cpu_usage = [10, 12, 15, 13, 18, 20, 17, 15, 14, 16]
memory_usage = [30, 32, 35, 33, 38, 40, 37, 35, 34, 36]

# 使用 numpy 计算
correlation_np = np.corrcoef(cpu_usage, memory_usage)[0, 1]
print(f"使用 NumPy 计算的相关系数: {
                correlation_np:.2f}")

# 使用 scipy 计算
correlation_scipy, _ = pearsonr(cpu_usage, memory_usage)
print(f"使用 SciPy 计算的相关系数: {
                correlation_scipy:.2f}")

13. 补充案例:结合 Flask 构建 Web 监控仪表盘

为了更直观地展示监控数据,我们可以使用 Flask(一个轻量级的 Web 框架)和 Chart.js(一个 JavaScript 图表库)构建一个简单的 Web 监控仪表盘。

项目结构:

system_monitor/
├── app.py         (Flask 应用)
├── templates/
│   └── index.html (HTML 模板)
└── static/
    ├── css/
    │   └── style.css  (样式)
    └── js/
        └── chart.min.js (Chart.js 库,从 https://www.chartjs.org/ 下载)
        └── app.js      (前端逻辑)

app.py (Flask 应用):

from flask import Flask, render_template, jsonify
import psutil
import time

app = Flask(__name__)

@app.route("/")
def index():
    return render_template("index.html")

@app.route("/data")
def get_data():
    cpu_percent = psutil.cpu_percent()
    memory_percent = psutil.virtual_memory().percent
    return jsonify(cpu_percent=cpu_percent, memory_percent=memory_percent)

if __name__ == "__main__":
    app.run(debug=True, host='0.0.0.0')  # 允许外部访问

templates/index.html (HTML 模板):

<!DOCTYPE html>
<html>
<head>
    <title>系统监控仪表盘</title>
    <link rel="stylesheet" href="{
             { url_for('static', filename='css/style.css') }}">
    <script src="{
             { url_for('static', filename='js/chart.min.js') }}"></script>

</head>
<body>
    <h1>系统监控仪表盘</h1>
    <div class="chart-container">
      <canvas id="cpuChart"></canvas>
    </div>
     <div class="chart-container">
        <canvas id="memoryChart"></canvas>
    </div>
    <script src="{
             { url_for('static', filename='js/app.js') }}"></script>
</body>
</html>

static/css/style.css (样式):

body {
            
    font-family: sans-serif;
    margin: 20px;
}

.chart-container {
            
    width: 80%;
    margin: 20px auto;
}

static/js/app.js (前端逻辑):

// CPU 图表
const cpuChartCtx = document.getElementById('cpuChart').getContext('2d');
const cpuChart = new Chart(cpuChartCtx, {
            
    type: 'line',
    data: {
            
        labels: [], // 时间标签
        datasets: [{
            
            label: 'CPU 使用率 (%)',
            data: [], // CPU 使用率数据
            borderColor: 'rgba(75, 192, 192, 1)',
            borderWidth: 2,
            fill: false
        }]
    },
    options: {
            
        scales: {
            
            y: {
            
                beginAtZero: true,
                max: 100
            }
        }
    }
});

// 内存图表
const memoryChartCtx = document.getElementById('memoryChart').getContext('2d');
const memoryChart = new Chart(memoryChartCtx, {
            
    type: 'line',
    data: {
            
        labels: [], // 时间标签
        datasets: [{
            
            label: '内存使用率 (%)',
            data: [], // 内存使用率数据
            borderColor: 'rgba(255, 99, 132, 1)',
            borderWidth: 2,
            fill: false
        }]
    },
    options: {
            
        scales: {
            
            y: {
            
                beginAtZero: true,
                max: 100
            }
        }
    }
});
// 更新图表数据的函数
function updateCharts() {
            
    fetch('/data')
        .then(response => response.json())
        .then(data => {
            
            const now = new Date().toLocaleTimeString(); // 获取当前时间

            // 更新 CPU 图表
            cpuChart.data.labels.push(now);
            cpuChart.data.datasets[0].data.push(data.cpu_percent);

            // 更新内存图表
            memoryChart.data.labels.push(now);
            memoryChart.data.datasets[0].data.push(data.memory_percent);


            // 限制数据点数量 (例如,最多显示 60 个点)
            const maxDataPoints = 60;
            if (cpuChart.data.labels.length > maxDataPoints) {
            
                cpuChart.data.labels.shift();
                cpuChart.data.datasets[0].data.shift();
            }

            if(memoryChart.data.labels.length > maxDataPoints){
            
                memoryChart.data.labels.shift();
                memoryChart.data.datasets[0].data.shift();
            }

            cpuChart.update(); // 更新图表
            memoryChart.update();
        });
}

// 每秒更新一次数据
setInterval(updateCharts, 1000);

运行步骤:

确保安装了 Flask 和 psutil: pip install flask psutil
下载 Chart.js: 从 https://www.chartjs.org/ 下载 chart.min.js,并将其放入 static/js/ 目录。
创建上述文件和目录结构。
运行 app.py: python app.py
在浏览器中访问: http://localhost:5000 (或你的服务器地址)

这个 Web 仪表盘会实时显示 CPU 使用率和内存使用率的折线图。

这只是一个非常基础的示例,你可以根据自己的需求进行扩展,例如:

添加更多图表(磁盘 I/O、网络流量等)。
使用更高级的图表库(如 D3.js、Plotly)。
添加交互功能(如选择时间范围、缩放)。
添加用户认证。
部署到服务器上。

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容