服务器运行时,监控各个客户端或服务器进程的内存使用情况可以了解各进程的内存占用情况,方便排查到具体进程的问题。
Windows
文件名:monitor.py
监控程序可以直接运行。各功能块功能已详细注释,更详细的解释可以查看文末参考博客,主要将同名的各个Python进程的占用情况全部监控,并将信息写入csv文件中。
"""
进程监控程序 for windows
python monitor.py --name test1
author: cyg
"""
import datetime
import os.path
import argparse
import psutil
import time
import pandas as pd
# 获取电脑整体的CPU、内存占用情况
def getMemory():
"""
Obtain the system memory usage
"""
data = psutil.virtual_memory()
memory = str(int(round(data.percent))) + "%"
print("系统整体memory占用:" + memory)
return memory
def getCpu():
"""
count each cpu usage's average
return: average cpu usage
"""
cpu_list = psutil.cpu_percent(percpu=True)
average_cpu = round(sum(cpu_list) / len(cpu_list), 2)
cpu = str(average_cpu) + "%"
print("系统各整体cpu平均占用:" + cpu)
return cpu
# 获取指定进程的CPU和内存占用信息代码
def getMemSize(pid):
"""
get each pid memory
"""
# 根据进程号来获取进程的内存大小
process = psutil.Process(pid)
memInfo = process.memory_info()
# rss: 该进程实际使用物理内存(包含共享库占用的全部内存)。
# vms:该进程使用的虚拟内存总量。
return memInfo.rss / 1024 / 1024
def getCpuPercent(pid):
"""
get each pid cpu usage percent
"""
# 根据进程号来获取进程的内存大小
p = psutil.Process(pid)
p_cpu = p.cpu_percent(interval=0.1)
cpu = round(p_cpu, 2)
return cpu
def getTotalM(processName, python_process):
"""
processName: cmp_server.py
python_process: 全体(pid, name)元组列表集和
[(124, 'cmp_server.py'), (234, 'cmp_server.py'),(3452, 'monitor.py')]
"""
# 一个进程名对应的可能有多个进程
# 进程号才是进程的唯一标识符,进程名不是
totalM = 0
for pid, name in python_process:
if name == processName:
totalM += getMemSize(pid)
print(f'{processName}进程占用内存:%.2f MB' % totalM)
finalM = round(totalM, 2)
return finalM
def getTotalCPU(processName, python_process):
"""
processName: cmp_server.py
python_process: 全体(pid, name)元组列表集和
[(124, 'cmp_server.py'), (234, 'cmp_server.py'),(3452, 'monitor.py')]
"""
# 一个进程名对应的可能有多个进程
# 进程号才是进程的唯一标识符,进程名不是
totalCPU = 0
for pid, name in python_process:
if name == processName:
totalCPU += getCpuPercent(pid)
totalCPU_convert = round(totalCPU, 2)
finalCPU = str(totalCPU_convert) + '%'
print(f"{processName}进程占用CPU:" + finalCPU)
return totalCPU_convert
# 将测试结果数据写入csv文件
def writeExcel(caseName, cpu, mem, processcpu, processmem, filename) -> None:
"""
All same name process which like 2 python process called 'cmp_server.py' will not be separate.
:param caseName: cmp_server.py
:param cpu: sys average cpu
:param mem: sys memory
:param processcpu: total cpu for process of the same name like 'cmp_server.py'
:param processmem: total mem for process of the same name like 'cmp_server.py'
:param filename: the name of csv
"""
timestamp = time.strftime('%Y-%m-%d-%H:%M:%S', time.localtime(time.time()))
dict = {'caseName': [caseName], 'Sys_CPU': [cpu], 'Sys_Memory': [mem],
'Pycharm_Cpu': [processcpu], 'Pycharm_Mem': [processmem],
'OperationTime': [timestamp]}
# 字典中的key值即为csv中列名
dataframe = pd.DataFrame(dict)
dataframe['OperationTime'] = pd.to_datetime(dataframe['OperationTime'])
csv_file = f"{filename}_monitor.csv"
if not os.path.exists(csv_file):
dataframe.to_csv(csv_file, date_format='%Y-%m-%d-%H:%M:%S',
mode='a', index=False, header=True, encoding='GBK')
else:
# 将DataFrame存储为csv, mode='a'表示每一次都是追加内容而不是覆盖,header=False表示不写列名
dataframe.to_csv(csv_file, date_format='%Y-%m-%d-%H:%M:%S',
mode='a', index=False, header=False, encoding='GBK')
# 封装方法为函数,以便后续直接调用
def getCpuAndMem(caseName, info, filename):
"""
caseName: 传入的不重复的.py文件名
info: 全体pid和name的元组列表
filename: csv文件名
"""
memory = getMemory()
cpu = getCpu()
# 获取pycharm64.exe进程占用的CPU和内存
processmem = getTotalM(caseName, info)
processcpu = str(getTotalCPU(caseName, info)) + '%'
writeExcel(caseName, cpu, memory, processcpu, processmem, filename)
print(f"{caseName}进程_CPU占用:%s {caseName}进程内存占用:%s MB" % (processcpu, processmem))
print("===============================================================")
def query_process(process_name):
"""
output current python process info
"""
init_name = set()
python_process = []
for proc in psutil.process_iter():
if process_name in proc.name():
# print("-------", proc.cmdline())
try:
for process in proc.cmdline()[1:]: # 第一个肯定是Python,故跳过
if len(process) >= 7:
if '/' in process:
name = process.split('/')[-1]
elif '\\' in process:
name = process.split('\\')[-1]
else:
name = process
break
python_process.append((proc.pid, name))
init_name.add(name)
except:
import traceback
traceback.print_exc()
return python_process, init_name
if __name__ == '__main__':
# python_process, name_list = query_process('python')
# print(python_process, name_list)
parser = argparse.ArgumentParser()
parser.add_argument('--name', type=str, default='test', help='inital csv name')
args = parser.parse_args()
print(f'chose filename_{args.name}')
continue_loop = True
while continue_loop:
t0 = time.time()
python_process, name_list = query_process('python') # 监控所有python进程状态
print(f'The name_list is {name_list}')
if len(name_list) == 1 and 'monitor.py' in name_list:
print('no python process!')
continue_loop = False
else:
name_list.discard('monitor.py')
for name in name_list:
getCpuAndMem(caseName=name, info=python_process, filename=args.name)
time.sleep(60) # 一分钟监控一次
t1 = time.time()
print(f'spend {round(t1-t0, 2)}s')
Linux
说明:和Windows完全一致,Linux输入以下指令可以后台运行 ,若遇其他问题可以边运行边调试,欢迎交流讨论。
nohup python -u monitor.py --name test > test.log 2>&1 &
运行结果展示
参考博客:Python神器:psutil库使用详解