我有一个ASP.NET应用程序,它通过创建和写入自定义性能计数器来跟踪统计信息.有时,我会在错误日志中看到指示计数器无法打开,因为它们已在当前进程中使用过.我认为这是由于我的.NET appdomain已在同一个w3wp.exe进程中重置.当我的应用程序域被回收时,如何避免这些错误并重新建立与性能计数器的连接?
柜台建设:
PerformanceCounter pc = new PerformanceCounter(); pc.CategoryName = category_name; pc.CounterName = counter_name; pc.ReadOnly = false; pc.InstanceLifetime = PerformanceCounterInstanceLifetime.Process; pc.InstanceName = instance_name;
柜台使用:
pc.Increment()
[2009年3月26日更新]收到的错误消息是:
实例'_lm_w3svc_1_root_myapp'已存在,其生命周期为Process.在删除它或使用它的进程退出之前,它不能重新创建或重用.已存在具有生命周期的过程.
我尝试通过初始化性能计数器并在瞬态AppDomain中写入其中一个来复制控制台应用程序中的异常.然后我卸载AppDomain并在第二个Appdomain中再次执行(相同的过程).他们都成功了.我现在不确定究竟是什么原因,我对ASP.NET中AppDomain回收的假设似乎是错误的.
使用基于进程的WCF性能计数器时,也可能在应用程序日志中遇到上述错误.这种情况的症状是应用程序事件日志中的三个错误块:
未加载性能计数器.
类别名称:ServiceModelService 3.0.0.0
计数器名称:调用
异常:System.InvalidOperationException:实例'abc@def | service.svc'已存在且生命周期为Process.在删除它或使用它的进程退出之前,它不能重新创建或重用.
这始终在系统事件日志中的以下信息消息之后立即发生:
进程ID为"nnnn"的工作进程为应用程序池"MyAppPool"提供服务已请求回收,因为工作进程已达到其允许的处理时间限制.
这会导致此服务的性能监视器计数器变得不可用,显然是几个小时.
该ServiceModelService 3.0.0.0
版本号将依赖于.NET您正在使用的版本(这是使用.net 3.5测试).
故障由工人流程达到其处理时间限制触发,此时必须回收.这是在IIS应用程序池回收设置(IIS 6.0及更高版本,因此Windows Server 2003及更高版本)中设置的.回收工作进程会导致基于新进程的性能计数器名称与旧进程冲突,从而产生错误.这是因为IIS使用重叠的回收,其中要终止的工作进程一直运行,直到新的工作进程启动.
再生产(在Windows Server 2003上测试= IIS 6.0)
创建WCF服务.
添加以下到web.config
的
部分:
在"属性"→"回收"下的服务的"应用程序池"属性中,将"回收工作进程"设置为1分钟.手动回收应用程序池没有任何效果,因为这不会从工作进程创建要求回收的请求(如带W3SVC
事件事件的事件查看器系统日志中所示).
在soapUI中,为WCF操作创建测试套件/测试用例/测试步骤/测试请求.
将测试用例添加到soapUI中的负载测试,或使用loadUI,以每秒1的速率触发调用.
每1分钟,工作进程将请求回收(在系统日志中很明显).每隔2分钟,这将导致应用程序日志中出现一批三个错误System.ServiceModel 3.0.0.0
.
在工作进程再次循环之前,该服务的性能计数器将不可用.(注意:此时将循环周期设置为更高的值,以查看性能计数器不可用的时间将实际回收进程并使计数器再次可用.)
可能的解决方案
修补程序KB981574取代修补程序KB971601.后一个修补程序描述了该问题:
FIX:监视应用程序的性能计数器停止响应应用程序退出并重新启动并在运行.NET Framework 2.0的计算机上收到System.InvalidOperationException异常
应用前一个修补程序不能解决问题.应用后一个修补程序导致应用程序池错误.
可以创建公开自定义服务主机的服务主机工厂:
using System; using System.Diagnostics; using System.ServiceModel; using System.ServiceModel.Activation; namespace MyNamespace { public class WebFarmServiceHostFactory : ServiceHostFactory { protected override ServiceHost CreateServiceHost( Type serviceType, Uri[] baseAddresses) { return new WebFarmServiceHost(serviceType, baseAddresses); } } public class WebFarmServiceHost : ServiceHost { public WebFarmServiceHost( Type serviceType, params Uri[] baseAddresses) : base(serviceType, baseAddresses) { } protected override void ApplyConfiguration() { base.ApplyConfiguration(); Description.Name = "W3wp" + Process.GetCurrentProcess().Id + Description.Name; } } }
并参考服务标记.svc
文件中的工厂:
<%@ ServiceHost Language="C#" Debug="true" Factory="MyNamespace.WebFarmServiceHostFactory" Service="WcfService1.Service1" CodeBehind="Service1.svc.cs" %>
不幸的是,虽然这使得问题发生的频率低得多,但仍然会发生(近似约30倍,尽管我没有测量到这方面的准确统计数据).
一个似乎完全解决问题的简单调整就是在之前添加一个sleep命令base.ApplyConfiguration();
:
Thread.Sleep(1);
这仅对每个工作进程回收一次,因此对服务性能的影响可以忽略不计.您可能必须提高此值(您可以将其设置为可配置),尽管1ms的最小设置对我有用(辩论该命令实际上要暂停多久).
在IIS中有一个DisallowOverlappingRotation
Metabase属性.这可以设置如下(给MyAppPool
应用程序池的示例):
cscript %SYSTEMDRIVE%\inetpub\adminscripts\adsutil.vbs SET w3svc/AppPools/MyAppPool/DisallowOverlappingRotation TRUE解决方案的比较
解决方案#3将导致您的站点在工作进程回收时由于没有IIS重叠回收而延长.
在使用5个线程每秒100次以上事务的基本Web服务的soapUI负载测试中,每次工作进程回收时,新事务被阻止的"冻结"显而易见.当测试更复杂的Web服务时,这种冻结会更长(8秒以上).
解决方案#2没有产生这样的阻塞,在循环期间具有平稳的响应流,并且没有产生perfmon冲突错误.
结论对于不要求低延迟的Web服务,可以使用解决方案#3.如果您知道负载分布和一天中的安静时间,您甚至可以在设定的时间将回收设置为每天(这可以在IIS中的相同选项卡中完成).如果使用Web场,这甚至可能会错开.
对于无法忍受此类延迟的Web服务,似乎解决方案#2是最好的前进方式.