一般的系统级别指标监控,更多关注CPU、内存、磁盘、网络等运行情况,对应用程序运行时的进程指标关注不够,导致不能深入了解系统运行状态。本文根据笔者应用实践,探讨一下进程级别监控涉及到的监控内容以及监控方式,供感兴趣的同行做参考。
一、 监控内容
众所周知,应用软件最终表现为应用程序,程序是指令、数据及其组织形式的描述,其本身没有任何运行的含义,是一个静态的概念;进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是程序的实体,是一个动态的概念。
进程的主要属性有:进程ID、进程名称、进程用户名称、进程状态、进程优先级、进程启动时间、包含的线程、使用的CPU时间、使用的内存、句柄等。进程的属性虽多,但根据笔者的应用实践,只要重点监控几个关键指标即可抓住进程的运行状态,进而对应用程序的健康状况做出正确的判断,并在发生故障前快速采取止损措施。
CPU
CPU描述了进程占用的计算资源,监控主要关注进程级别CPU的两种场景:High CPU、Low CPU。
当进程长时间处于High CPU状态时,除了正常负载高的情况,程序内部可能:
a.在不断产生大量异常
b.发生了死循环
c.在频繁做垃圾回收(Garbage Collection,GC)
当进程长时间处于Low CPU状态时,程序内部可能:
a.发生了死锁
b.发生了阻塞
2.内存
内存描述了进程占用的存储资源,由于Windows资源分为托管资源和非托管资源,因此监控进程级别的内存,应该关注同时包含这两种资源的提交内存(Commit Size)。
监控主要关注进程级别High Memory场景,当进程长时间处于High Memory状态时,程序内部可能:
a.发生了内存泄漏,对象一直被使用,无法及时释放
b.产生了内存碎片,导致内存溢出(Out Of Memory,OOM)
3.端口数
端口数描述了进程占用的网络资源,通过命令“netstat -ano”可以获得整个机器的网络端口数占用情况,但无法直接获得每个进程的网络端口数占用情况,因此需要对“netstat -ano”输出的进程ID做分组汇总,以便获得每个进程占用的端口数,从而对进程的监控深入一个层次。
当进程占用的端口数过高时,程序内部可能发生了端口泄漏。
4.活跃线程数
进程是线程的容器,一个进程可以包括多个线程,进程和线程都是CPU工作时间段的描述。线程具有生命周期,因此具有很多状态(新建、就绪、运行、阻塞、死亡),从Windows任务管理器看到的某个进程包含的线程数,是所有状态的线程数,监控粒度比较粗。
进程级别的线程监控,应该关注活跃线程数,也就是真正在运行的线程数,这可以通过如下方法得到:
private int GetActiveThreadCount()
{
int MaxWorkerThreads, miot, AvailableWorkerThreads, aiot;
ThreadPool.GetMaxThreads(out MaxWorkerThreads, out miot);
AvailableWorkerThreads = aiot = 0;
ThreadPool.GetAvailableThreads(out AvailableWorkerThreads, out aiot);
return MaxWorkerThreads - AvailableWorkerThreads;
}
当进程包含的活跃线程数过高时,程序内部可能发生了线程泄漏。
5.同一进程组流量分布
前面的几个指标关注的是单一进程,当不同机器上的进程组成一个集群时,需要同时关注这些进程组间的指标,比如流量分布。
当一个集群负载均衡的处理请求时,流量应该是均分到每一个处理进程的,如果监控到某一个进程处理的TPS远小于整个集群的平均TPS时,该进程很可能发生了阻塞或宕机
6.可用性指标
可用性指标是指可以作为度量系统死活点(Dead Live Point,DLP)的指标,一般关注两个方面:进程可用性、进程上运行的服务可用性。
进程可用性主要判断进程是否仍然存活,可以通过调用系统接口判断进程是否存在,也可以通过进程埋点上报心跳信息。
进程上运行的服务可用性主要关注服务运行的成功率或失败率,因此涉及到的业务因素比较多,限于篇幅不再展开。如果整个系统基于微服务框架,比如高速服务框架(High Speed Framework,HSF),则可以对服务调用进行统一监控,从而可以统一计算服务可用性。
二、 监控方式
对进程级别的监控由于比较底层,一般采用两种手段:监控主动收集、进程埋点上报。
监控主动收集