使用基于进程的 WCF 性能计数器时,应用程序日志中也可能会遇到上述错误。其症状是应用程序事件日志中的三个错误块:
未加载性能计数器。
类别名称:ServiceModelService 3.0.0.0
计数器名称:调用
异常:System.InvalidOperationException:实例“abc@def|service.svc”已经存在,其生命周期为进程。在它被删除或使用它的进程退出之前,它不能被重新创建或重用。
这总是在系统事件日志中的以下信息消息之后立即发生:
服务应用程序池“MyAppPool”的进程 ID 为“nnnn”的工作进程已请求回收,因为该工作进程已达到其允许的处理时间限制。
这会导致该服务的性能监视器计数器变得不可用,显然有几个小时。
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
中<system.serviceModel>
:
<diagnostics performanceCounters="All" />
- 在属性 → 回收下服务的应用程序池属性中,将回收工作进程设置为 1 分钟。手动回收应用程序池没有任何效果,因为这不会从工作进程创建回收请求(在带有
W3SVC
信息事件的事件查看器系统日志中很明显)。
- 在soapUI中为WCF 操作创建一个测试套件/测试用例/测试步骤/测试请求。
- 将测试用例添加到soapUI 中的负载测试中,或者使用loadUI,以每秒 1 次的速率触发调用。
- 每 1 分钟,工作进程将请求一次回收(在系统日志中很明显)。每 2 分钟,这将导致来自
System.ServiceModel 3.0.0.0
.
- 在工作进程再次回收之前,该服务的性能计数器将变得不可用。(注意此时将回收周期设置为更高的值以查看性能计数器不可用的时间将实际回收进程并使计数器再次可用。)
可能的解决方案
解决方案 1 - 红鲱鱼
修补程序KB981574取代了修补程序KB971601。后一个修补程序描述了该问题:
修复:当应用程序退出并重新启动并且您在运行 .NET Framework 2.0 的计算机上收到 System.InvalidOperationException 异常时,监视应用程序的性能计数器停止响应
应用以前的修补程序并不能解决问题。应用后一个修补程序会导致应用程序池错误。
解决方案 2 - 一个可行的解决方案
可以创建一个公开自定义服务主机的服务主机工厂:
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 的最小设置对我有用(关于该命令实际休眠多长时间的争论)。
解决方案 3 - 最简单的修复
在 IIS 中有一个DisallowOverlappingRotation
Metabase Property。这可以设置如下(MyAppPool
应用程序池的示例):
cscript %SYSTEMDRIVE%\inetpub\adminscripts\adsutil.vbs SET w3svc/AppPools/MyAppPool/DisallowOverlappingRotation TRUE
解决方案比较
解决方案#3 将导致您的站点在工作进程由于没有 IIS 重叠回收而进行回收时停机更长时间。
在使用 5 个线程每秒处理 100 多个事务的基本 Web 服务的 soapUI 负载测试中,每次工作进程回收时,新事务被阻止的“冻结”是明显的几秒钟。当测试更复杂的 Web 服务时,这种冻结时间更长(超过 8 秒)。
解决方案#2 没有产生这样的阻塞,在回收期间有流畅的响应流,并且没有产生性能冲突错误。
结论
对于不需要低延迟的 Web 服务,可以使用解决方案 #3。如果您知道一天中的负载分布和安静时间,您甚至可以将回收设置为每天在设定的时间(这可以在 IIS 的同一选项卡中完成)。如果使用网络农场,这甚至可以错开。
对于无法容忍此类延迟的 Web 服务,似乎解决方案 #2 是最好的方法。