dstat 是一个不错的系统监控程序,类似于 vmstat、iostat、mpstat,所不同的是,可以监控多个系统指标,如 CPU、网络、内存、中断等,可以将结果显示到终端,也可保存到文件。
另外,该程序是通过 Python 实现的,可以动态指定插件,甚至编写自己的插件。
在本文中,大致介绍其使用方法,以及实现的原理。
使用方法
详细的使用方式可以查看 man 帮助,在此只是介绍一些 dstat 的特性。
查看 man 帮助时,就会发现,作者的原意是要替换掉 vmstat、iostat、ifstat 等工具。通过该工具可以将多个监控在一块展示,而且显示比较友好,并且可以直接将统计结果保存。
另外,dstat 支持高亮显示,不过需要注意的是,颜色并非标示系统指标的好或者坏;对于颜色改变,只是说明其单位修改了,或者超过了某个范围 (通常对于 cpu 而言)。
其帮助手册可以通过 man 1 dstat
查看,命令的基本格式如下,也可以通过 -h
选项查看。
dstat [-afv] [options..] [delay [count]]
常用选项:
-c, --cpu
统计CPU状态,包括 user, system, idle (空闲等待时间百分比), wait (等待磁盘IO),
hardware interrupt (硬件中断), software interrupt (软件中断) 等;
-d, --disk
统计磁盘读写状态,主要包括了读写信息;
-l, --load
统计系统负载情况,包括1分钟、5分钟、15分钟平均值;
-m, --mem
统计系统物理内存使用情况,包括used, buffers, cache, free;
-s, --swap
统计swap已使用和剩余量;
-n, --net
统计网络使用情况,包括接收和发送数据;
-p, --proc
统计进程信息,包括runnable、uninterruptible、new;
-N eth1,total
统计eth1接口汇总流量;
-r, --io
统计I/O请求,包括读写请求;
-y, --sys
统计系统信息,包括中断、上下文切换;
-t
显示统计时时间,对分析历史数据非常有用;
--fs
统计文件打开数和inodes数;
其中最后两个参数表示,每隔 delay 秒显示一行,总计 count 行,默认值是 1s 和空 (无限)。如果 delay 大于 1 ,默认该行还是每隔 1s 更新一次,当超过 delay 秒后,换行。
需要注意的是,当 delay > 1
时,每秒的更新值是显示的之前统计的均值,如 delay=10 ,则会显示 1 秒的均值,2 秒的均值,…,也就是最终结果仍然是秒级的均值。
如果同时设置了 --noupdate
,则确实是每隔 delay 秒更新一次,而不会每秒更新一次。
常用参数
如下是常见的使用方法,每隔 3 秒刷新。
$ dstat -clmdngyr 3
使用插件
Dstat 是基于插件的,程序本身提供了定时显示的功能,而显示的内容可以通过插件进行扩展,从而可以很方便的添加自己所需要的插件。
如下的示例是使用 time、cpu、disk 三个插件,其作用是等效的。
$ dstat -tcd
$ dstat --time --cpu --disk
$ dstat -M time,cpu,disk
$ dstat -M time -M cpu -M disk
可以使用的插件包括了 dstat.py
文件中定义的插件,以及在 plugins 目录下定义的插件,使用时可以直接通过 --PLUG-NAME
指定相应的插件执行。
通过 dstat --list
查看当前支持的所有插件及其保存的目录,其中最简单的是 plugins/dstat_helloworld.py
,也就是一个示例,可以通过 dstat --helloworld
执行。
如果想要自己定义插件,可以直接参考 plugins/dstat_helloworld.py
的实现。
其它
除了直接监控性能外,还可以放到其它的程序中使用,其中在 examples/{read.py,mstat.py}
,包含了简单的示例,没有仔细研究过。感兴趣的可以自己检查下。
源码分析
dstat 的源码可以通过 yumdownloader 下载,或则直接到 Dstat 官网 上下载,当然也可以从 Github 上下载 。在官网,除了 Dstat 之外,还有些其它的工具可供参考。
dstat 中的定时执行模块是通过 sched 模块实现的,因此之前我们先看看该模块的功能。详细文档可以参考官方 sched — Event scheduler,在此仅简单介绍一下。
sched 模块
该模块实际是一个调度器,也就是延时处理机制,每次想要定时执行某任务都必须写入一个调度,详细内容参考 Python 官方。
如果查看源码,可以发现其实现非常简单,会通过 timefunc
计算时间,休眠通过 delayfunc
实现,而且通过 queue 保存多个事件。
使用时通常包含了三步:
1. 生成调度器
scheduler = sched.scheduler(timefunc, delayfunc)
第一个参数是一个可以返回时间戳的函数;第二个参数可以在定时未到达之前的阻塞函数。通常来说,我们会使用 time.time
、time.sleep
,当然也可以使用自定义的定时函数。
2. 加入调度事件
scheduler.enterabs(time, priority, action, argument)
scheduler.enter(delay, priority, action, argument)
对于 enter() 函数来说,采用的时相对时间,其四个参数分别为 A) 间隔时间;B) 优先级,当多个任务同时到达时使用,数字大的优先级高; C) 被调用回调的函数;D) 传递给回掉函数的参数。
enterabs() 使用的是绝对时间,该参数需要与初始化时的 timefunc 入参格式相同。
3. 运行
scheduler.run()
运行所有的调度事件,该函数会等待下一个事件,然后执行他直到没有可调度的事件为止。
示例
可以参考如下的简单示例。
#!/usr/bin/env python
#-*- coding:utf-8 -*-
import time
import sched
def foobar(msg): # 被定时调度的函数
print "Current:", time.time(), 'MSG ', msg
s = sched.scheduler(time.time, time.sleep) # 初始化
s.enter(2, 2, foobar, ("High Priority.",)) # 添加如下的两个调度任务
s.enter(2, 1, foobar, ("Low Priority.",))
s.run() # OK, Run it
print "Current:", time.time()
time.sleep(3)
注意,sched 模块不是循环的,一次调度被执行后就 Over 了,如果想再执行,请再次 enter,也就是如上的函数只会调度一次。
如果要循环执行,可以使用如下的函数。
#!/usr/bin/env python
#-*- coding:utf-8 -*-
import time
import sched
def foobar(msg, act_time): # 被定时调度的函数
global s
print "Current:", time.time(), 'MSG ', msg
s.enterabs(act_time + 5, 1, foobar, ("Again.", act_time + 5,))
s = sched.scheduler(time.time, time.sleep) # 初始化
initime = time.time()
s.enterabs(initime + 1, 1, foobar, ("Begin.", initime + 1,)) # 添加调度任务
s.run() # OK, Run it
print "Current:", initime
time.sleep(100)
在 dstat 中,就是使用的后者。
注意,上述程序只为了说明,生产环境的程序中一定不能在定时函数中再次调用 enter()
函数,否则调用栈会不断增加,直到内存上限。
框架
其中所有的插件都是继承于 class dstat
类,包括了一些必须的成员变量,以及相应的接口函数,其中部分是必须要包含的,如果没有则会在初始化的时候报错。
插件分析
dstat
的插件包括了 dstat.py
中的类,以及 plugins
目录下的插件。
在内部的代码中,所有的插件必须要以 dstat_
开头,后面的是插件的名称。在加载的时候,会将命令行传入的 -
转换为 _
,如 --disk-util
实际对应 plugins/dstat_disk_util.py
文件。
插件查找时,首先会搜索 dstat.py
文件中的实现方法,也就是开头为 dstat
的类,然后搜索 pluginpath
目录下的 dstat_xxx.py
文件,所有的插件可以通过 listplugins()
查看。
循环调度
初始化的操作是在 main()
函数中完成,对于插件,则会通过 execfile()
、exec
加载。
对于调度,如上所述,调度任务是通过 sched 实现的。
def main():
... ...
scheduler = sched.scheduler(time.time, time.sleep)
inittime = time.time()
### Let the games begin
while update <= op.delay * op.count or op.count == -1:
scheduler.enterabs(inittime + update, 1, perform, (update,))
scheduler.run()
sys.stdout.flush()
update = update + interval
linecache.clearcache()
... ...
最后执行是 perform()
函数。
执行、显示
如下是主要的执行函数,首先会调用各个插件的 extract()
调用,该函数会对参数进行解析,并将结果保存在 val
中。
然后会调用通用函数 show()
、showend()
进行打印,dstat 会通过颜色对状态值进行着色,该功能是通过 cprint()
函数实现,可以参考其细节。
def perform(update):
... ...
### Calculate all objects (visible, invisible)
line = newline
oline = ''
for o in totlist:
o.extract()
if o in vislist:
line = line + o.show() + o.showend(totlist, vislist)
if op.output and step == op.delay:
oline = oline + o.showcsv() + o.showcsvend(totlist, vislist)
### Print stats
sys.stdout.write(line + theme['input'])
if op.output and step == op.delay:
outputfile.write(oline + '\n')
... ...
总结
介绍到此为止,该工具基本上就是提供了一个通用的框架,可以很好的进行扩展,细节方面可以直接看下源码了。
参考
可以参考 Dstat Offical Site,以及关于 Dstat的介绍 。