C#中PerformanceCounter获取IIS站点Requests/Sec值不更新问题
嘿,我懂你遇到的糟心事——之前用PerformanceCounter都好好的,偏偏拿特定IIS站点的"Requests/Sec"时,数据死活不更新,但性能监视器里明明显示正常,而且程序已经有管理员权限了对吧?咱们一步步捋清楚问题出在哪:
问题排查与修复步骤
1. 实例名称匹配的大小写坑
先看你代码里的这段:
cat.GetInstanceNames().First(s => s.Contains("_lm_w3svc_3_root").ToUpper())
这里的.ToUpper()放错地方啦!你是把Contains的结果(true/false)转成大写,而不是把实例名称s转成大写来匹配。IIS生成的计数器实例名通常是大写的_LM_W3SVC_3_ROOT,大小写不匹配的话,你可能拿到了错误的实例,自然拿不到正确的更新数据。改成这样才对:
cat.GetInstanceNames().First(s => s.ToUpper().Contains("_LM_W3SVC_3_ROOT"))
或者更严谨的不区分大小写比较:
cat.GetInstanceNames().First(s => s.Equals("_LM_W3SVC_3_ROOT", StringComparison.OrdinalIgnoreCase))
2. 别每次都新建PerformanceCounter实例!
这是最常见的坑——PerformanceCounter的NextValue()方法第一次调用永远返回0,因为它需要两次采样来计算差值(比如每秒请求数是两次采样的差除以时间间隔)。你每次调用GetRequest()都新建一个计数器实例,每次调用NextValue()都是第一次,当然拿不到更新的数据!
正确的做法是把计数器实例做成静态的,在程序启动时初始化一次,后续复用它:
// 静态字段保存计数器实例,只初始化一次 private static readonly PerformanceCounter _requestCounter; // 在类的静态构造函数里初始化计数器 static YourClassName() { var cat = PerformanceCounterCategory.GetCategories() .FirstOrDefault(s => s.CategoryName.Contains("ASP.NET Apps v4.0.30319")); if (cat == null) throw new InvalidOperationException("找不到ASP.NET Apps v4.0.30319计数器类别"); var instanceName = cat.GetInstanceNames() .First(s => s.Equals("_LM_W3SVC_3_ROOT", StringComparison.OrdinalIgnoreCase)); _requestCounter = new PerformanceCounter( "ASP.NET Apps v4.0.30319", "Requests/Sec", instanceName, true); // 第一次调用NextValue()做初始化,返回0,后续调用才会拿到真实的计算值 _requestCounter.NextValue(); } public static int GetRequest() { // 复用已初始化的计数器,每次调用NextValue()都会返回最新的计算值 return unchecked((int)_requestCounter.NextValue()); }
3. 先确认你要的实例真的存在
有时候IIS站点重启、应用池回收后,计数器实例名可能会变,你可以先把所有实例名打印出来,确认你要的那个确实在列表里:
var cat = PerformanceCounterCategory.GetCategories() .First(s => s.CategoryName.Contains("ASP.NET Apps v4.0.30319")); foreach (var instance in cat.GetInstanceNames()) { Console.WriteLine($"找到实例:{instance}"); }
这样能避免你选到错误的实例,白忙活一场。
4. 权限兜底检查
虽然你说程序有管理员权限,但还是确认下:运行程序的用户是否在Performance Monitor Users组里?不过既然性能监视器能正常读取,这个概率不高,但排查的时候可以顺便确认下,排除权限问题。
总结
最可能的两个原因就是:每次新建计数器实例导致NextValue()永远返回初始0,以及实例名称大小写匹配错误。按照上面的步骤调整代码,复用计数器实例,修正匹配逻辑,应该就能拿到实时更新的请求数了!
内容的提问来源于stack exchange,提问作者DaCh

