jle*_*lew 5 .net asp.net performancecounter appdomain
我有一个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;
Run Code Online (Sandbox Code Playgroud)
柜台使用:
pc.Increment()
Run Code Online (Sandbox Code Playgroud)
[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)
web.config
的<system.serviceModel>
部分:<diagnostics performanceCounters="All" />
W3SVC
事件事件的事件查看器系统日志中所示).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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
并参考服务标记.svc
文件中的工厂:
<%@ ServiceHost Language="C#" Debug="true" Factory="MyNamespace.WebFarmServiceHostFactory" Service="WcfService1.Service1" CodeBehind="Service1.svc.cs" %>
不幸的是,虽然这使得问题发生的频率低得多,但仍然会发生(近似约30倍,尽管我没有测量到这方面的准确统计数据).
一个似乎完全解决问题的简单调整就是在之前添加一个sleep命令base.ApplyConfiguration();
:
Thread.Sleep(1);
Run Code Online (Sandbox Code Playgroud)
这仅对每个工作进程回收一次,因此对服务性能的影响可以忽略不计.您可能必须提高此值(您可以将其设置为可配置),尽管1ms的最小设置对我有用(辩论该命令实际上要暂停多久).
在IIS中有一个DisallowOverlappingRotation
Metabase属性.这可以设置如下(给MyAppPool
应用程序池的示例):
cscript %SYSTEMDRIVE%\inetpub\adminscripts\adsutil.vbs SET w3svc/AppPools/MyAppPool/DisallowOverlappingRotation TRUE
Run Code Online (Sandbox Code Playgroud)
解决方案#3将导致您的站点在工作进程回收时由于没有IIS重叠回收而延长.
在使用5个线程每秒100次以上事务的基本Web服务的soapUI负载测试中,每次工作进程回收时,新事务被阻止的"冻结"显而易见.当测试更复杂的Web服务时,这种冻结会更长(8秒以上).
解决方案#2没有产生这样的阻塞,在循环期间具有平稳的响应流,并且没有产生perfmon冲突错误.
对于不要求低延迟的Web服务,可以使用解决方案#3.如果您知道负载分布和一天中的安静时间,您甚至可以在设定的时间将回收设置为每天(这可以在IIS中的相同选项卡中完成).如果使用Web场,这甚至可能会错开.
对于无法忍受此类延迟的Web服务,似乎解决方案#2是最好的前进方式.
IIRC,IIS 不会确保您的第一个 AppDomain 在启动第二个 AppDomain 之前关闭,特别是当您手动或自动回收它时。我相信,当启动回收时,首先实例化第二个AppDomain,一旦成功,新的传入请求将定向到它,然后IIS等待第一个AppDomain(正在关闭的那个)完成处理它的任何请求有。
结果是存在两个 AppDomain 的重叠,两个 AppDomain 的 值相同instance_name
。
然而,并非所有问题都得到解决。我已通过将进程 ID 作为实例名称的一部分包含在代码中纠正了此问题。但它似乎引入了另一个问题——我认为进程范围的性能计数器似乎永远不会在不重新启动计算机的情况下消失。(这可能是我的一个错误,所以 YMMV)。
这是我创建实例名称的例程:
private static string GetFriendlyInstanceName()
{
string friendlyName = AppDomain.CurrentDomain.FriendlyName;
int dashPosition = friendlyName.IndexOf('-');
if (dashPosition > 0)
{
friendlyName = friendlyName.Substring(0, dashPosition);
}
friendlyName = friendlyName.TrimStart('_');
string processID = Process.GetCurrentProcess().Id.ToString();
string processName = Process.GetCurrentProcess().ProcessName;
string instanceName = processName + " " + processID + " " + friendlyName.Replace('/', '_').Trim('_').Trim();
return instanceName;
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
4025 次 |
最近记录: |