0

我遇到了一种奇怪的情况,即在服务器重新启动后立即使用 WMI 或 ADSI 从 Windows 服务(用 C# 编写,设置为“自动”启动)启动应用程序池挂起。

我将描述这个问题:

我们正在开发一个大型应用程序(Windows 2003 Server SP2,IIS 6.0),其中包含以下主要进程(这些进程在应用程序启动时使用 Windows 服务启动过程调用和初始化):

1) XServer1.exe, XServer2.exe - 这些进程是本机 COM-Exe 服务器,包含一些逻辑,但主要通过 DCOM 向其他进程提供 COM 对象(主要是 .NET2COM interOp 调用和纯 COM 调用)。例如,一些经典的 ASP“应用程序范围静态对象”(w3wp.exe)是在这些进程中“活动”的 COM 对象。

2) dllhost.exe - 这是一个 COM+ 应用程序。我们的一些 DLL 被加载到这个充当“状态服务器”的进程中(与 ASP.NET 进程外会话服务器的想法相同,但适用于经典 ASP 页面)。

3) 3 个不同的 IIS 应用程序池(我们称它们为 appPool1\2\3)——我们的 ASP 页面、ASP.NET 页面、WCF 服务等的容器。这些应用程序池中的代码(本机 C++ COM dll 和 C#)(w3wp .exe's) 通常对 (1) 和 (2) 中描述的进程进行 DCOM 调用。只有 appPool1 可以配置为 Web Garden。

为了启动\停止我们的应用程序,我们编写了一个 Windows 服务 (C#) 来控制这些过程。我们的服务进程称为 XWinService.exe。该服务依赖于以下 Windows 服务(列表从前 4 个服务开始,正在进行的尝试使列表像这样......):

W3SVC

aspnet_state

COMSysApp

DcomLaunch

winmgmt

兰曼服务器

兰曼工作站

秒记

浏览器

期限服务

应用的Stop过程总结(由服务实现):

1) 停止所有 3 个 IIS 应用程序池 (appPool1\2\3) - 这样做是为了防止 w3wp.exe 进程在应用程序关闭时跳活。这是使用 C# (system.Management.dll) 中的 WMI 实现的

2) 停止 XServer1\2.exe

3) 停止 COM+ 应用程序 (dllhost.exe)。

应用的Start过程总结(由服务实现):

1) 执行 Stop 过程 - 这确保没有 HTTP 命中将在时间之前唤醒 w3wp.exe 进程。

2) 调用和初始化 XServer1\2.exe COM-Exe 服务器 - 在任何 w3wp.exe 调用之前都需要初始化。只有在某些对象被初始化后,w3wp.exe 才能访问这些服务器。这是由 .NET2COM InterOp(最终是 DCOM)实现的。

3) 调用和初始化 dllhost.exe(COM+ 应用程序)进程 - 这是由 ComAdmin 目录 API (C#) 实现的。

4) 启动我们的 3 个应用程序池 - 这允许传入的 HTTP 命中唤醒 w3wp.exe 进程并开始服务请求。

这是负责启动\停止应用程序池 (WMI) 的 C# 代码。此代码在我们的服务进程 (XWinService.exe) 中运行:

    ConnectionOptions co = new ConnectionOptions(); 
ManagementScope scope = new ManagementScope(@"\\localhost\root\MicrosoftIISV2", co); 
foreach (string appPool in AppPools) 
{ 
   string objPath = string.Format("IISApplicationPool.Name='W3SVC/AppPools/{0}'", appPool); 
   using (ManagementObject mc = new ManagementObject(objPath)) 
  {
    mc.Scope = scope; 
    if (Operation.ToLower() == "start") 
    {
     mc.InvokeMethod("Start", null, null); // ### The problematic line of code ### 
    } 
    else if (Operation.ToLower() == "stop") 
   { 
      mc.InvokeMethod("Stop", null, null); 
   } 
   else if (Operation.ToLower() == "recycle") 
  {
     mc.InvokeMethod("Recycle", null, null); 
  }

 } 

}

 </p>

现在的问题:

在重新启动服务器之前,手动启动服务(从 services.msc 工具)成功,没有任何问题。另外,停止它是可以的。我们将服务设置为“自动”启动,即在服务器(Win2K3 SP2)启动并重新启动服务器时启动。当服务器启动(出现登录屏幕)时,我们的服务“卡住”(状态 =“正在启动”)并且永远不会(它挂起 2 天!)启动。

分析过程揭示了以下几点:

1) XWinService.exe 进程卡在有问题的代码行(### 上面的###)。这挂了 2 天,直到我们终止了该进程。请注意:关闭应用程序池(启动过程以停止过程开始)没有挂起!

2) 在此“挂起”期间从 XWinService.exe 获取的 DUMP 文件(使用 DebugDiag 工具)我们可以看到正在等待的线程。这是它的(本机)堆栈跟踪:

    Thread 6 - System ID 2784
Entry point mscorwks!Thread::intermediateThreadProc
Create time 11/19/2009 1:40:05 PM
Time spent in user mode 0 Days 00:00:00.078
Time spent in kernel mode 0 Days 00:00:00.781


This thread is making a COM call to multi-threaded apartment (MTA) in process 884
Function Source
ntdll!KiFastSystemCallRet  
ntdll!NtRequestWaitReplyPort+c  
rpcrt4!LRPC_CCALL::SendReceive+230  
rpcrt4!I_RpcSendReceive+24  
ole32!ThreadSendReceive+138  
ole32!CRpcChannelBuffer::SwitchAptAndDispatchCall+112  
ole32!CRpcChannelBuffer::SendReceive2+d3  
ole32!CAptRpcChnl::SendReceive+ab  
ole32!CCtxComChnl::SendReceive+1a9  
rpcrt4!NdrProxySendReceive+43  
rpcrt4!NdrClientCall2+206  
rpcrt4!ObjectStublessClient+8b  
rpcrt4!ObjectStubless+f  
….  

该线程正在调用(通过 DCOM)进程 884 中的组件,即 svchost.exe,运行以下服务:AeLookupSvc、AudioSrv、Browser、CryptSvc、dmserver、EventSystem、helpvc、lanmanserver、lanmanworkstation、Schedule、seclogon、SENS、ShellHWDetection , TrkWks, winmgmt, wuauserv, WZCSVC。

如您所见,“winmgmt”服务(负责WMI)在此进程中运行,我们的服务依赖于它,因此我们的服务将在winmgmt启动后启动(对于IIS W3SVC服务也是如此)。

svchost.exe 进程(884)被转储,我们可以看到一个线程(等待 DCOM 调用结束)访问进程 2880,即 wmiprvse.exe(我猜这是 WMI 服务器。不知道它是否相关,但此过程有 2 个实例)。这是线程的本机调用堆栈(在 svchost.exe 中):

     Thread 48 - System ID 3816
Entry point wbemcore!CCoreQueue::_ThreadEntry
Create time 11/19/2009 1:40:56 PM
Time spent in user mode 0 Days 00:00:00.00
Time spent in kernel mode 0 Days 00:00:00.00


This thread is making a COM call to multi-threaded apartment (MTA) in process 2880

Function Source
ntdll!KiFastSystemCallRet 
ntdll!NtRequestWaitReplyPort+c 
rpcrt4!LRPC_CCALL::SendReceive+230 
rpcrt4!I_RpcSendReceive+24 
ole32!ThreadSendReceive+138 
ole32!CRpcChannelBuffer::SwitchAptAndDispatchCall+112 
ole32!CRpcChannelBuffer::SendReceive2+d3 
ole32!CAptRpcChnl::SendReceive+ab 
ole32!CCtxComChnl::SendReceive+1a9 
… 

3)将我们的服务设置为“手动”并启动它(手动 - 在登录服务器后或在重新启动后立即从不同的服务器远程启动它)是好的 - 没有任何挂起。

4) 我们删除了我们的服务(从注册表中!),并在 Windows“启动”文件夹中放置了一个批处理文件。此批处理文件调用服务的代码,但将其作为普通的 C# 可执行文件运行。服务器重新启动后,它也挂在相同的有问题的代码行上(再次...... 2天,直到我们杀死它)。

5) 使用 ADSI (System.DirectoryServices) 而不是 WMI 具有相同的结果(启动应用程序池挂起!)。

在过去的两周里,我们一直在研究这个......

我的问题:

==========

1)有人遇到同样的问题吗?

2)有谁知道它为什么挂起?我们应该考虑到任何额外的服务依赖性吗?

3)有没有人有这个问题的解决方案?

4) 为什么只有在服务设置为“自动”启动时才会在重启后发生这种情况?如果我们手动进行 - 一切正常!

***** 小更新:**

我们注意到,在虚拟机(VMware 站)上,服务在重新启动后平均挂起约 40 分钟,直到它启动(注意:它永远不会启动失败,但 40 分钟太长了)。系统事件日志中记录了一条事件日志消息,指出我们的服务挂起超过 16 分钟(来源:服务控制管理器,事件 ID:7044)。

在“常规”机器(真正的金属)上,服务开始前的平均时间约为 55 小时!!!同样,如上所述记录事件日志条目。

平均值是根据 10 个不同的 VM 和 8 个不同的“真实”服务器计算得出的。

4

1 回答 1

0

我看到没有人回复,但无论如何我会发布一些消息......

我们发现,在启动应用程序池之前,将服务状态设置为“已启动”并打开一个new Thread(...)运行上述代码的新线程 ( )(使用 WMI 启动应用程序池)可以解决问题。

这是OnStart服务方法的伪代码:

OnStart {
     StopProcedure();

     InvokeInitXServer1And2(); //COM-Exe servers

     InvokeInitCOMPlusApplication(); //dllhost.exe

     SetServiceStatus(SERVICE_STARTED);

     Thread worker = new Thread(new threadStart(IISAppPoolStartWMI); //Calls the code
}

这是服务在合理时间内启动的唯一方式(最长 3 分钟,真实机器和虚拟机的平均约 1.5 分钟!)并启动 w3wp.exe 进程。

如果有人对此有解释(MTA\STA 问题?!?!?)我会很高兴阅读它。

于 2009-11-23T01:33:17.423 回答