asp.net 跨AppDomain队列使用自定义性能计数器

wj8zmpe1  于 2023-10-21  发布在  .NET
关注(0)|答案(6)|浏览(90)

我有一个ASP.NET应用程序,它通过创建和写入自定义性能计数器来跟踪统计数据。有时,我会在错误日志中看到,计数器无法打开,因为它们已在当前进程中使用。我认为这是由于我的.NET应用程序域已在同一个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。在删除它或使用它的进程退出之前,不能重新创建或重用它。已经存在,其生命周期为Process。
我尝试在控制台应用程序中复制该异常,方法是初始化性能计数器并在一个 transient AppDomain中写入其中一个计数器。然后卸载AppDomain并在第二个Appdomain中再次执行此操作(相同的进程)。他们都成功了。我现在不确定到底是什么原因造成的,我关于AppDomain在ASP.NET中回收的假设似乎是错误的。

wqlqzqxt

wqlqzqxt1#

使用process based WCF performance counters时,在应用程序中也可能遇到上述错误。此问题的症状是应用程序事件日志中出现三个错误:
未加载性能计数器。
分类名称:3.0.0.0
柜台名称:电话
异常:System.InvalidOperationException:示例'abc@def| service.svc“已存在,生存期为Process。在删除它或使用它的进程退出之前,不能重新创建或重用它。
此操作总是在系统事件日志中的以下信息消息之后立即发生:
为应用程序池“MyAppPool”提供服务的进程ID为“nnnn”的辅助进程已请求回收,因为该辅助进程已达到其允许的处理时间限制。
这将导致此服务的性能监视器计数器在几个小时内不可用。
ServiceModelService 3.0.0.0版本号将是depend on the version of .NET you are using(这是使用.NET 3.5测试的)。

背景

该错误由工作进程达到其处理时间限制触发,此时必须回收该工作进程。这是在IIS应用程序池回收设置中设置的(IIS 6.0及更高版本,因此是Windows Server 2003及更高版本)。回收辅助进程会导致新的基于进程的性能计数器名称与旧的冲突,从而产生错误。这是因为IIS使用overlapped recycling,其中要终止的工作进程保持运行,直到新的工作进程启动。

复制

  • (在Windows Server 2003 = IIS 6.0上测试)*
  • 创建WCF服务。
  • the following添加到<system.serviceModel>部分中的web.config

<diagnostics performanceCounters="All" />

  • 在Properties → Recycling下的服务的Application Pool属性中,将Recycle worker process设置为1 minute。手动回收应用程序池没有任何效果,因为这不会从工作进程创建回收请求(如包含W3SVC信息事件的事件查看器系统日志中所示)。
  • soapUI中为WCF操作创建一个测试套件/测试用例/测试步骤/测试请求。
  • 将测试用例添加到soapUI中的负载测试中,或者使用loadUI,以每秒1次的速率触发调用。
  • 每隔1分钟,工作进程将请求一次回收(在系统日志中很明显)。每隔2分钟,这将在System.ServiceModel 3.0.0.0的应用程序日志中产生一批三个错误。
  • 该服务的性能计数器将变得不可用,直到辅助进程再次重新运行。(注意:此时将回收周期设置为更高的值,以查看性能计数器在多长时间内不可用,实际上将回收进程并使计数器再次可用。)

可能的解决方案

解决方案1 -红鲱鱼

热修复KB981574取代热修复KB971601。后一个修复程序描述了该问题:
FIX:当应用程序退出并重新启动时,监视应用程序的性能计数器停止响应,并且您在运行.NET Framework 2.0的计算机上收到System.InvalidOperationException异常
应用前一个修复程序不能解决问题。应用后一个修复程序导致应用程序池错误。

方案2 -工作方案

可以创建service host factory that exposes a custom service host

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倍,尽管我没有测量到准确的统计数据)。
一个简单的调整似乎可以完全解决这个问题,就是在base.ApplyConfiguration();之前添加一个sleep命令:

Thread.Sleep(1);

这在每个工作进程回收时只触发一次,因此对服务性能的影响可以忽略不计。您可能必须提高此值(您可以使其可配置),尽管最小设置为1 ms对我有效(关于此命令实际休眠多长时间的争论放在一边)。

解决方案3 -最简单的修复

在IIS中有一个DisallowOverlappingRotation Metabase Property。此can be set as follows(针对MyAppPool应用程序池给出的示例):

cscript %SYSTEMDRIVE%\inetpub\adminscripts\adsutil.vbs SET w3svc/AppPools/MyAppPool/DisallowOverlappingRotation TRUE

解决方案对比

解决方案#3将cause your site to be down longer时,工作进程由于缺乏IIS重叠回收而重新启动。
在soapUI负载测试中,一个基本的Web服务每秒使用5个线程处理100多个事务,每次工作进程回收时,都会出现几秒钟的“冻结”,即新事务被阻塞。当测试更复杂的Web服务时,冻结时间更长(8秒以上)。
解决方案#2没有产生这种阻塞,在回收期间具有平滑的响应流,并且没有产生性能冲突错误。

结论

对于不要求低延迟的Web服务,可以使用解决方案#3。如果你知道负载分布和一天中的安静时间,你甚至可以将回收设置为每天在一个设定的时间进行(这可以在IIS的同一个选项卡中完成)。如果使用网络农场,这甚至可以错开。
对于不能容忍这种延迟的Web服务,解决方案2似乎是最好的方法。

4sup72z8

4sup72z82#

IIRC,IIS不会确保在启动第二个AppDomain之前关闭第一个AppDomain,特别是当您手动或自动回收它时。我相信,当启动回收时,第二个AppDomain首先被示例化,一旦成功,新的传入请求将被定向到它,然后IIS等待第一个AppDomain(正在关闭的那个)完成处理它的任何请求。
结果是存在两个AppDomain的重叠,两者都具有相同的instance_name值。
然而,并非所有问题都得到解决。我在代码中纠正了这个问题,将进程ID作为示例名的一部分。但它似乎引入了另一个问题--我认为是进程范围的性能计数器似乎永远不会在不重新启动计算机的情况下消失。(这可能是我的一个bug,所以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;
    }
jhkqcmku

jhkqcmku3#

我不是自定义计数器的Maven,但根据您提供的信息,我认为这是值得一试的,考虑到一些代码在添加域即将被回收时尝试使用计数器的可能性。在任何与dispose或析构函数相关的东西中查找计数器的用法。

mepcadol

mepcadol4#

如果延迟创建性能计数器,则可能是线程问题。在进程回收之后,如果同时发生两个页面命中(这并不让我感到惊讶),那么您的性能创建调用可能会运行多次。一方面,您可以安全地忽略此错误。但是如果你想消除它,我建议你用一个lock语句来 Package 你的性能计数器代码生成,比如

lock (this.lockObject)
{
 //Create performance counter
}
vc9ivgsu

vc9ivgsu5#

我遇到了类似的问题:多示例,进程生存期计数器不能在Visual Studio中创建一次以上,但这是因为我打开了PerfMon!
我花了一段时间才意识到。

qeeaahzv

qeeaahzv6#

IIS保证您的第一个AppDomain在启动第二个AppDomain之前不会关闭,特别是当您手动或自动回收它时,如果您将应用程序池回收“禁用重叠回收”属性设置为false(默认值)。
在共享主机的情况下,您可以在web.config文件中定义。

相关问题