news 2026/9/8 7:15:03

C#实现IIS监控插件:实时检查站点与应用程序池状态

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C#实现IIS监控插件:实时检查站点与应用程序池状态

简介:check_iis是一款用C#编写的IIS监控插件,专为需要在Icinga2、Icinga、Centreon、Shinken、Naemon等Nagios系监控平台中巡检Windows IIS站点的运维人员设计。它在本机执行Sites与AppPool检查,需由代理或执行程序以管理员身份调用,并支持.NET 3.5及4.0+环境。资源共18个文件,以8个cs源码文件为主,另含config/conf配置、sln解决方案、csproj工程文件、XML及license等,压缩包仅36KB,结构紧凑便于直接阅读和二次开发。目前已有200人学习下载。通过这份资源,读者可以快速上手check_iis的配置与编译,理解站点和应用程序池匹配的命名开关及大小写规则,也可基于源码调整监控逻辑,适合对Windows监控插件开发有兴趣的工程师参考学习。

1. 项目概述:check_iis 到底在监视什么

先说明一下背景。做服务器运维或者偏运维的开发,几乎都遇到过这种情况:生产环境里的 IIS 站点毫无征兆地挂了,用户反馈打不开页面,你打开服务器一看,发现应用程序池(AppPool)已经处于“已停止”状态。更头疼的是,你根本不知道它是何时停止的,也没办法第一时间知道——只能等用户来报障。

check_iis 这个插件项目,要解决的就是这类问题。它是一个用 C# 编写的监控插件,专门盯住本地计算机上的 IIS 站点和应用程序池,把它们的运行状态、响应情况、资源占用实时采集出来。它的定位很明确:轻量级、易部署、围绕 IIS 场景做深做透,而不是像大型监控平台那样什么都管。

它的适用人群非常清晰:负责 Windows Server + IIS 运维的工程师、写 C# 上位机或者内部工具、需要把 IIS 状态接入到现有监控体系的开发人员,还有那些被“半夜站点挂了没人知道”折腾过的朋友。如果你只是管理一两台服务器,手动打开 IIS 管理器看一眼也行;但如果你有几十个站点、十几个 AppPool,人肉巡检完全不现实,这类小工具的价值就体现出来了。

我自己的看法是,这个项目最难能可贵的不是技术本身有多高级,而是它把一个运维痛点拆得很清楚:不是“监控整个服务器”,而是“只监控 IIS 站点和 AppPool”,范围小、目标明确、代码量可控、可维护性强。这种做小做精的思路,恰恰是很多半吊子监控项目缺少的。

2. 监视 IIS 的技术路径选型,为什么是这几种方案

要写一个 IIS 监控插件,第一步不是写代码,而是确定怎么去拿 IIS 的状态数据。Windows 平台上,C# 开发者有往下这几条成熟的技术路径可选,我对比后列出了各自的优劣势。

2.1 三条主流技术路线对比

技术方案获取的数据范围权限要求开发难度适用场景
Microsoft.Web.Administration站点、AppPool 状态和配置信息管理员权限低,API 封装得很友好插件首选通用方案
WMI 查询进程、服务、性能计数器等系统数据视查询类目而定中,查询语句需要调试需要额外拿性能指标时组合使用
System.Diagnostics.PerformanceCounterCPU、内存、连接数等运行时指标管理员权限深入采集性能数据时的补充手段

2.2 为什么核心逻辑放在 Microsoft.Web.Administration

Microsoft.Web.Administration 是微软官方提供的 IIS 管理 API,ServerManager 类就是整个操作的入口。它用起来有点像操作一个“IIS 的内存快照”,把站点集合和应用程序池集合都暴露给你,直接遍历就能拿到每个实体和它的 State 属性。

用这个 API 最大的理由是省心。你不用去解析 IIS 的配置文件(applicationHost.config)——虽然本质上这个 API 就是把配置映射成了对象模型,但它帮你把序列化和反序列化做了。而且 State 属性不是简单的字符串,是一个 ObjectState 枚举,取值有 Started、Stopping、Stopped、Starting、Unknown 这几种,判断逻辑非常干净。相比自己去读 WMI 里 Win32_Service 的 State 字段,再用数字状态码映射,这种方式出错概率小得多。

提示:关键细节是,ServerManager 每次实例化都会重新读取配置。如果你在循环里反复 new ServerManager,性能会很差。正确做法是只实例化一次,操作完统一释放。

2.3 性能计数器作为补充

仅靠 Microsoft.Web.Administration,能拿到的是“状态”数据——运行着还是停着,这属于是/否的判断,够用,但信息量不够。真正要判断站点是不是“健康”,还需要连接数、当前请求数、最近一分钟的请求成功率这类运行时数据。这时候就要用到性能计数器。

IIS 相关的性能计数器类别主要是 Web Service 和 W3SVC_W3WP。Web Service 类别下面有针对每个站点(以站点名称命名的实例)的 Current Connections、Total Bytes Sent、Total Method Requests 等计数器;W3SVC_W3WP 则按照工作进程实例提供 CPU 和内存占用数据。

用 PerformanceCounter 这个类读取即可,核心代码并不复杂:

PerformanceCounter counter = new PerformanceCounter( "Web Service", "Current Connections", "Default Web Site", "."); float currentConnections = counter.NextValue();

注意一个问题:某些计数器第一次调用 NextValue() 返回的是 0,需要间隔一小段时间再取第二次,才能拿到真实值。这不是 bug,是计数器机制本身就是这么设计的——差值型计数器必须经过两次采样才能算出速率。所以插件在设计采样逻辑时,一定要考虑“预热”的过程。

从方案选型来看,我实际的建议是:主体用 Microsoft.Web.Administration,如果你只是想知道“站点挂没挂”,这一个就够了;但如果你想把插件接到 Zabbix 或自己的告警系统里,加上性能计数器,让告警信息里不仅写着“站点停了”,还写着“停止前连接数异常飙升”,对定位问题会有很大帮助。

3. 核心实现:写一个可靠的 check_iis 插件

明确了技术路径,下面进入正题。我这里展示的是我在类似项目里沉淀下来的一套实现方式,整体代码量不大,但每一步都有值得注意的细节。

3.1 项目结构与依赖引入

先用 .NET 创建一个控制台应用,目标框架建议用 .NET 6 或 .NET 8,这样在 Windows Server 2019/2022 上部署比较省事,也方便做单文件发布。项目的主要依赖只有一个:

dotnet add package Microsoft.Web.Administration

这个包在 NuGet 上直接可用,但是有个潜在的坑:它默认依赖系统的 IIS 组件。如果开发机器上根本没装 IIS,ServerManager 在某些操作下会抛异常。所以最好在装了 IIS 的机器上开发,或者退一步在开发机上安装 IIS 管理脚本和工具功能。

项目结构可以拆成三个核心文件:

  • Program.cs:入口,负责参数解析和调度。
  • IisMonitor.cs:核心监控逻辑,封装 ServerManager 和性能计数器操作。
  • Reporter.cs:输出结果,支持 Console 和文件两种方式。

3.2 读取 AppPool 状态的核心代码

应用程序池状态检测是整个插件最核心、也是运维最关心的功能。我直接给出一个可用的实现:

using Microsoft.Web.Administration; public class AppPoolStatus { public string Name { get; set; } public string State { get; set; } public string RuntimeVersion { get; set; } public string ManagedPipelineMode { get; set; } public long? CurrentWorkerProcessId { get; set; } } public class IisMonitor : IDisposable { private readonly ServerManager _serverManager; public IisMonitor() { _serverManager = new ServerManager(); } public List<AppPoolStatus> GetAppPoolStatuses() { var result = new List<AppPoolStatus>(); foreach (ApplicationPool pool in _serverManager.ApplicationPools) { var status = new AppPoolStatus { Name = pool.Name, State = pool.State.ToString(), RuntimeVersion = pool.ManagedRuntimeVersion, ManagedPipelineMode = pool.ManagedPipelineMode.ToString() }; // 尝试获取工作进程 ID,便于精确定位问题 try { WorkerProcess wp = pool.WorkerProcesses.FirstOrDefault(); status.CurrentWorkerProcessId = wp?.ProcessId; } catch { // 某些系统权限不足时拿不到 WorkerProcesses,这里不阻塞主流程 status.CurrentWorkerProcessId = null; } result.Add(status); } return result; } public void Dispose() { _serverManager.Dispose(); } }

注意三个细节。一是遍历 ApplicationPools 拿 State 很快,但 pool.WorkerProcesses 是懒加载的,如果池子已经停止了,它返回空集合是正常现象,不用当成异常处理。二是每次检测完一定要释放 ServerManager,否则会有句柄泄漏,长时间跑下来内存持续增长。三要注意 ManagedPipelineMode 的取值是 Integrated 还是 Classic,有些老站点跑在 Classic 模式下,如果监控脚本把 Classic 当异常报警,就会产生大量误报——这个我在后面常见问题里还会展开。

3.3 读取站点状态与绑定的实现

站点检测除了状态,还要关注绑定和端口是否有效。有些场景下站点显示已启动,但因为端口被其他程序占用了,实际上服务不可用——如果监控逻辑里没有端口探测这个环节,根本发现不了这种问题。

我建议在状态检测之外,加一个 TCP 连通性检查:拿到站点的绑定信息(IP、端口、主机头),然后用 TcpClient 尝试连接。这个检查是锦上添花,但能把监控深度提升一个层次。

public List<SiteStatus> GetSiteStatuses() { var result = new List<SiteStatus>(); foreach (Site site in _serverManager.Sites) { var status = new SiteStatus { Name = site.Name, State = site.State.ToString(), Bindings = site.Bindings.Select(b => $"{b.Protocol}://{b.BindingInformation}").ToList() }; // 若站点已启动,额外做端口连通性验证 if (site.State == ObjectState.Started) { status.IsPortOpen = CheckPort(site.Bindings.FirstOrDefault()?.EndPoint); } result.Add(status); } return result; }

注意:BindingInformation 的格式是“IP:端口:主机头”,比如*:80:www.example.com。如果你用字符串去解析端口,务必用EndPoint属性,这个属性是 API 帮你解析好的,直接拿 Port 即可,不要自己 split 字符串——自己解析在 IPv6 地址上会翻车。

3.4 输出格式与告警集成设计

插件毕竟是给人用的,输出格式直接决定实用程度。我见过一些工具把原始 JSON 哗啦啦地输出到终端,看的人头皮发麻。check_iis 我建议做成“参数决定输出格式”的方式:

  • --apppool:只检测应用程序池,输出状态摘要。
  • --site:只检测站点。
  • --json:输出结构化 JSON,便于接入 Zabbix、Prometheus 的 exporter 机制。
  • --threshold:可选,CPU/内存使用率告警阈值。

具体输出代码长这样:

static void PrintSummary(List<AppPoolStatus> pools, List<SiteStatus> sites) { Console.WriteLine("===== IIS Monitor Summary ====="); Console.WriteLine($"[AppPools] Total: {pools.Count}, Started: {pools.Count(p => p.State == "Started")}"); foreach (var pool in pools.Where(p => p.State != "Started")) { Console.WriteLine($"[WARN] AppPool '{pool.Name}' is {pool.State}"); } Console.WriteLine($"[Sites] Total: {sites.Count}, Started: {sites.Count(s => s.State == "Started")}"); foreach (var site in sites.Where(s => s.State != "Started" || !s.IsPortOpen)) { Console.WriteLine($"[WARN] Site '{site.Name}' is {site.State}, PortOpen={site.IsPortOpen}"); } }

输出逻辑的核心思路是只高亮异常项。正常状态下输出简短、安静,异常时给出明确的项目名和状态值。这个设计经验来自真实运维场景——前半夜没人盯着屏幕看完整日志,必须让异常信息一眼跳出来。

4. 实操过程:编译、部署与运行验证

代码写完了,落地部署环节同样有很多讲究。这里把我实际操作中的完整流程走一遍。

4.1 发布与部署要点

发布时,建议直接做单文件发布:

dotnet publish -c Release -r win-x64 --self-contained false -p:PublishSingleFile=true

这里我特别强调:Framework-dependent 模式(即--self-contained false)在服务器上最省心,因为只需要目标机器装了对应版本的 .NET Runtime,发布文件体积很小。但前提是你要确保生产服务器上已经装好了对应版本的 .NET。如果你不想依赖服务器环境,就用--self-contained true,发布产物会大一些,但拷过去就能跑。运维场景里两条路都有人走,没有绝对的对错。

部署目录我建议放在C:\Tools\CheckIis\,路径不要带空格,不要放中文目录,否则后续接入计划任务或者第三方监控软件时,可能出现引号转义问题。别问我怎么知道的——我在这上面踩过一次,路径带空格导致 Nagios 的远程命令传参时被拆成了两段。

4.2 权限配置:为什么必须管理员权限

check_iis 在运行的时候,有两个地方牵扯到权限。

一是通过 Microsoft.Web.Administration 读取 AppPool 的 WorkerProcesses,这个操作需要管理员权限。普通用户打开这个集合时,不会直接报错,而是返回空集合——这很容易让人误判为“当前没有工作进程”。

二是性能计数器的读取,需要用户属于“Performance Monitor Users”组。

所以部署的时候,我建议用这两种方式处理:

  • 手动巡检:直接用管理员身份运行命令行。
  • 计划任务:把插件配成每隔5分钟运行一次,计划任务里勾选“使用最高权限运行”,并且使用专用服务账号而不是当前登录用户。

如果只是在命令行里手动跑一次,可以这样验证:

CheckIis.exe --site --json

成功的话会输出类似下面的内容:

{ "Sites": [ { "Name": "Default Web Site", "State": "Started", "Bindings": ["http://*:80:"], "IsPortOpen": true } ] }

如果看到 IsPortOpen 为 false 而 State 是 Started,说明站点监听异常,优先排查端口占用和防火墙规则。

4.3 接入计划任务的配置示例

用计划任务做定时巡检是最轻量的集成方案,不需要额外装任何服务。创建一个每5分钟运行一次的计划任务,命令行参数设置成--site --apppool --json,然后把输出重定向到日志文件。

这里的要注意的一个细节是:计划任务里重定向输出和手动命令行里不一样,必须通过 cmd /c 包一层,否则输出重定向会不生效:

cmd /c "C:\Tools\CheckIis\CheckIis.exe --site --apppool --json > C:\Tools\CheckIis\logs\check_result.log 2>&1"

然后你就可以在服务器上配一个简单的文件监控,当 check_result.log 中出现"State": "Stopped"或者"IsPortOpen": false时触发告警。如果你的团队用的是 Zabbix,也可以用 Zabbix Agent 的自定义 key 来调用这个插件,把输出解析成监控项。

5. 常见问题与排查技巧实录

实际使用过程中,总有一些文档不会明确写的细节。这节我整理成速查表,并展开讲讲几个高频问题的排查思路。

5.1 高频问题速查表

现象可能原因排查方式
运行时报 UnauthorizedAccessException当前用户不是管理员用管理员身份运行或检查计划任务的“使用最高权限运行”
状态栏显示 Started 但端口探测失败端口被占用或站点绑定的是特定主机头netstat -ano;浏览器按主机头访问试试
AppPool 显示已停止,但回收后自动停止应用程序池的失败计数超限查看事件查看器里 WAS 事件的退出码
性能计数器读出来一直是 0计数器未预热第一次 NextValue() 后延时1秒再读第二次
网站在服务器上正常,远程访问 503AppPool 的“启用32位应用程序”设置与站点不匹配核对站点应用的位数,切换 AppPool 的 32 位开关

5.2 经典案例:站点没挂,但就是访问不了

我遇到过最典型的“隐蔽故障”是这样的:IIS 管理器里站点显示“已启动”,AppPool 也显示“已启动”,但客户端访问时直接报 503 Service Unavailable。用插件一检查,状态全绿,但 IsPortOpen 正常——因为 80 端口确实有监听,只是监听者不是 IIS 的工作进程。

排查到最后发现,是服务器上装了另一个 Web 服务,把 80 端口抢走了。IIS 因为启动时端口绑定失败,但状态没来得及更新,就出现了这种“逻辑上已启动、物理上没监听”的诡异局面。这个案例给我最大的教训是:监控 IIS 不能只用 API 读状态,必须结合端口探测。这也是我在前面的代码里强烈建议加 TcpClient 检查的原因——它能抓出这些 API 看不到的异常.

另一个值得注意的坑:IIS 站点绑定里如果设置了非 80 端口,而你写死了只检查 80,那么每次检测都会误报“站点不可用”。要解决这个问题,最稳妥的办法是直接读取 site.Bindings 拿到实际端口,再去做 TCP 连接检测。别写任何硬编码端口。

5.3 关于 IIS 版本差异的注意事项

不同版本的 IIS,插件表现差异很大。

IIS 7.0 / 7.5(Windows Server 2008 / 2008 R2)老机器上,Microsoft.Web.Administration 读取 ApplicationPool 的状态有一些历史包袱:当你用 ServerManager 实例化后,随意修改配置再保存,可能意外改写 applicationHost.config 的格式,导致整个 IIS 配置失效。所以,检查类工具读取就纯读取,不要调用 CommitChanges()——这是一个铁律。即使只读,在老版本上也要小心:ServerManager.Dispose() 之前如果开启了编辑模式,内部状态没有正确释放,仍可能触发写操作。你不确定的时候,直接用 using 包住,不要手动管理生命周期。

Windows Server 2012 及以上的 IIS 8.0+,这些坑都少了很多,API 也稳定了,可以直接放心用。

还有一点:IIS 响应头里默认会暴露 X-Powered-By: ASP.NET 和 Server 版本信息。内部监控不影响,但这属于基本的安全加固项。我在部署监控插件时习惯顺带把 IIS 的响应头版本信息隐藏掉,操作位置在 IIS 管理器的“HTTP 响应标头”模块,或者直接配置 web.config:

<system.webServer> <httpProtocol> <customHeaders> <remove name="X-Powered-By" /> </customHeaders> </httpProtocol> <security> <requestFiltering removeServerHeader="true" /> </security> </system.webServer>

这个配置只针对单个站点。如果你想在服务器级别全局移除,就需要在 applicationHost.config 里做配置,或者下载专门的 URL Rewrite 模块来处理。这算一个小的加分项,和 check_iis 配合使用效果更好——站点本身的安全状态也在监控范围之外做了加固。

6. 最后的几个经验和补充技巧

分享几个我在实际使用中攒下的细节经验,不一定都在代码里,但都直接影响插件好不好用。

第一,采集频率不要贪快。IIS 站点和 AppPool 的状态不是一个瞬息万变的数据,没必要 1 秒钟查一次。5 分钟一次足够覆盖绝大多数故障场景。查得太频繁反而会给服务器带来额外的资源开销,尤其在应用池很多(50个以上)的情况下,ServerManager 的初始化成本不可忽略。如果你确实需要秒级感知故障,应该在 AppPool 回收时间或者事件查看器上做文章,而不是靠轮询。

第二,告警一定要带上下文。检查结果里如果只输出“Default Web Site 已停止”,收到告警的同事还是会一脸懵——是手动停止的?还是崩溃导致的?有没有对应的 Windows 事件日志?所以建议在插件输出里,顺便调用一句 PowerShell 去查最近的系统事件,把 WAS 或 W3SVC 来源的最近几条错误事件附在告警信息后面。

Get-WinEvent -FilterHashtable @{LogName='System'; ProviderName='WAS'; StartTime=(Get-Date).AddMinutes(-10)} | Select-Object -First 5

这个信息拼在一起,排查效率能翻倍。

第三,如果要做多服务器监控,不建议直接在一台机器上跑多个插件进程。更好的做法是把 check_iis 做成“被监控端”,通过约定的命令行接口输出 JSON,由统一的监控平台去拉取。这样每台服务器上只部署一个轻量 agent 或计划任务,数据汇总在上层完成,架构会清爽很多。

最后再分享一个小技巧:开发这类运维小工具时,保持参数解析的简单直观很重要。我之前做过一版“智能”插件,可以根据机器名自动判断要监控哪些站点,结果部署到新服务器上总是不符合预期,排查了半天发现是配置文件格式的问题。后来干脆改成最简单的显式参数传递,反而再也没出过错。在运维工具的设计里,“不要聪明过头”从来都是美德。

check_iis 这个项目本身不大,但如果能真正打磨到稳定、无脑、可复用,它的价值就会远远超过那几百行代码。希望这篇拆解对你有参考作用——改天如果你在写类似的监控小工具时遇到坑,欢迎回来一起聊聊。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/8 7:14:40

PLC编程与通讯实战:从梯形图到Modbus TCP、Profinet的底层逻辑

前几天后台的热搜词几乎都被PLC相关的词占满了&#xff0c;从西门子、三菱到台达、汇川、信捷&#xff0c;从“PLC编程入门”到“Profinet通讯”“Modbus TCP服务器”“LabVIEW监控”……说实话&#xff0c;这个老物件在工控圈的生命力比很多人想象中旺盛得多。这几年我一直在现…

作者头像 李华
网站建设 2026/9/8 7:14:39

3D-ResNets-PyTorch实战:视频动作识别原理与迁移学习全指南

简介&#xff1a;这是面向计算机视觉和视频理解研究者的三维ResNet动作识别实现&#xff0c;源自CVPR 2018论文&#xff0c;核心解决视频中人类行为分类与时空特征提取问题&#xff0c;适合刚接触视频理解的研究生以及需要算法落地的工程师。代码基于PyTorch重构&#xff0c;支…

作者头像 李华
网站建设 2026/9/8 7:14:27

DeepSeek技术路线图解析:开源AGI与国产芯片机遇

这次我们来深入解读梁文锋在DeepSeek投资者交流会上的核心观点。这场3小时44分的交流不仅揭示了DeepSeek的技术路线图&#xff0c;更重要的是为国产芯片和开源生态指明了发展方向。从会议内容看&#xff0c;DeepSeek展现出了难得的克制——不盲目追求参数规模&#xff0c;而是聚…

作者头像 李华
网站建设 2026/9/8 7:10:15

Java并发编程全解析:从三性到锁、线程池与面试实战

最近在面试别人时碰到一个挺典型的场景&#xff1a;简历上写着"精通并发编程"&#xff0c;结果聊到volatile和synchronized的区别&#xff0c;答了"一个修饰变量一个修饰方法"就卡住了。再追问一句"那synchronized在JDK 1.6之后到底优化了什么"&…

作者头像 李华
网站建设 2026/9/8 7:10:00

为 NAND 续命:页隔离技术如何让“坏块”重获新生

1. 引言&#xff1a;NAND 的寿命焦虑与坏块现实NAND Flash 是今天几乎所有电子设备存储的基础。从手机里的 UFS、电脑里的 SSD&#xff0c;到数据中心中的企业级盘&#xff0c;再到工业设备中的 eMMC&#xff0c;NAND 凭借高密度、低功耗和非易失性成为主流选择。然而&#xff…

作者头像 李华
网站建设 2026/9/8 7:09:52

SpringBoot学生选课管理系统:从业务设计到并发控制的完整实践

每年到了毕业设计选题季&#xff0c;技术社区里总会出现同一个问题&#xff1a;“Java 毕设选什么题好&#xff1f;”而“基于 SpringBoot 的学生选课管理系统”几乎是所有候选列表里的常客。乍一看这个题目有点老套——选课管理&#xff0c;网上源码一大把&#xff0c;还有什么…

作者头像 李华