蛙蛙推荐:编写一个服务监控的软件
如果一个服务被部署到了几十台机器上,我们往往需要每天花费很多的时间去查看每台机器上的服务的运行状况,虽然微软有MOM(Microsoft Operations Manager)和SMS(Systems Management Server),但处于成本上及其它方面的考虑,好多时候我们还用不上这些东西,其实微软公开了好多管理和监控方面的API和工具,比如WMIC,System.Managerment等,把这些零散的API和工具集中起来,便可以开发一些满足自定义需求的小软件。
我们要实现以下任务
1、确认指定的进程已启动
2、获取指定机器,指定进程的CPU、内存、线程使用情况
3、确认指定机器的指定端口在监听
4、获取指定机器指定计数器的情况
5、获取指定机器上的系统日志/应用日志
6、获取指定服务相关trace
7、确认指定服务的功能拨测能通过
8、抓取指定机器指定进程的dump
9、在指定机器上进行网络抓包
10、重启指定机器上的某服务,某应用程序进程池等
下面来一一考虑一下
1、确认指定的进程已启动
可以用WMI接口来获取远程机器的进程列表,然后遍历这个列表,确认指定服务的进程名字是否在这个列表里,核心代码(来自网络)如下:
public static DataTable RunningProcesses()


{

// The second way of constructing a query
string queryString =
"Select Name, ProcessId, Caption, ExecutablePath" +
" FROM Win32_Process";

SelectQuery query = new SelectQuery(queryString);
ConnectionOptions options = new ConnectionOptions();
options.Username = @"administrator";
options.Password = "";

ManagementScope scope = new System.Management.ManagementScope(@"\\.\root\CIMV2");
scope.Connect();

ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query);
ManagementObjectCollection processes = searcher.Get();

DataTable result = new DataTable();
result.Columns.Add("Name", Type.GetType("System.String"));
result.Columns.Add("ProcessId", Type.GetType("System.Int32"));
result.Columns.Add("Caption", Type.GetType("System.String"));
result.Columns.Add("Path", Type.GetType("System.String"));

foreach (ManagementObject mo in processes)

{
DataRow row = result.NewRow();
row["Name"] = mo["Name"].ToString();
row["ProcessId"] = Convert.ToInt32(mo["ProcessId"]);
if (mo["Caption"] != null)
row["Caption"] = mo["Caption"].ToString();
if (mo["ExecutablePath"] != null)
row["Path"] = mo["ExecutablePath"].ToString();
result.Rows.Add(row);
}
return result;
}
2、获取指定机器,指定进程的CPU、内存、线程使用情况
这几样数据,我们用计数器来获取,具体表格如下
某进程的CPU使用量:process类别下的% Processor Time,实例名是你服务的进程名字(不加后缀名)
某进程的内存使用量:process类别下的Private Bytes,实例名是你服务的进程名字(不加后缀名)
某进程的内存使用量:process类别下的Thread Count,实例名是你服务的进程名字(不加后缀名)
其它的计数器的说明请打开perfmon工具一个一个看每个计数器的说明文字
关于获取某机器某计数器数值的代码大约如下
public class PerfCounter


{
public PerfCounter(string categoryName,
string counterName,
string instanceName,
string machineName
)

{
this.categoryName = categoryName;
this.counterName = counterName;
this.instanceName = instanceName;
this.machineName = machineName;
}

public string categoryName;
public string counterName;
public string instanceName;
public string machineName;
}
public class PerfCounterHelper


{
public static string GetPerfCount(List<PerfCounter> counters)

{
List<PerformanceCounter> pcs = new List<PerformanceCounter>();
foreach (PerfCounter counter in counters)

{
pcs.Add(new PerformanceCounter(counter.categoryName,
counter.counterName,
counter.instanceName,
counter.machineName));
}
StringBuilder text = new StringBuilder();
try

{
int i = 0;
while(true)

{
Thread.Sleep(1000);
i++;
text.AppendFormat("第{0}次采样\r\n", i);
foreach (PerformanceCounter pc in pcs)

{
text.AppendFormat("{0} \t{1}\t{2} \r\n",pc.CounterName,pc.InstanceName, pc.NextValue());
}
if (i > 3) break;
}
}
catch (Exception ex)

{
Trace.WriteLine(ex);
}
finally

{
foreach (PerformanceCounter pc in pcs)

{
pc.Close();
}
}

return text.ToString();
}
}
使用如下
List<PerfCounter> counters = new List<PerfCounter>();
counters.Add(new PerfCounter("Processor", "% Processor Time", "_Total", "."));
counters.Add(new PerfCounter("Memory", "Pages/sec", "", "."));
Console.WriteLine(PerfCounterHelper.GetPerfCount(counters));
其实获取每个进程的内存,线程,CPU等信息,用WMI查询Win32_Process也可以得到,但像CPU等信息还要往出算,所以简单的办法就是通过计数器获取了
3、确认指定机器的指定端口在监听
一般提供网络接口的服务都要监听一个或者几个端口,我们手工来确认端口是否监听的时候一般是登录到那台机器上运行netstat -na | find "LISTENING"来查看输出,或者在本机上用telnet 192.168.0.1 80来看是否能打开,其实我们用程序也能调用者两个命令,但是调用远程机器上的netstat命令,你一般没办法获取它的输出,通常的办法是把输出结果用命名管道重定向到某个文件,然后以编程的方式去访问网络路径去取这个文本文件并读取文本,这种方式太麻烦。而以编程的方式调用telnet需要交互,也很麻烦,所以我们用socket来建立一个连接来测试远程端口是否在监听,大约如下
public static bool IsListenPort(string remoteHost, int port)


{
Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
try

{
s.Connect(Dns.GetHostAddresses(remoteHost)[0], port);
![]()