使用 WMI 管理 IIS 7 中的工作进程和 AppDomain

作者:Tim Ammann

借助 WMI 脚本,可以相对轻松地在 IIS 中管理工作进程和应用程序域 (AppDomain)。 IIS 工作进程由 Windows 进程激活服务 (WAS) 生成,由 W3wp.exe 执行。 工作进程可以包含通常为响应 .aspx 页请求而创建的 AppDomain。

本文介绍了如何只编写几行 VBScript 代码来完成以下任务:

  • 查看工作进程中当前正在执行的请求
  • 获取所有工作进程的状态
  • 卸载特定 AppDomain 或所有 AppDomain
  • 显示所有 AppDomain 及其属性

前几个步骤

  1. 确保已启用 IIS 和脚本。

    a. 如果使用 Windows Vista,请依次打开“控制面板”、“程序和功能”,然后打开“Windows 功能”。 在“Web 管理工具”下,选择“IIS 管理脚本和工具”以启用脚本。 b. 如果使用的是 Windows Server® 2008,请打开“服务器管理器”。 使用“添加角色向导”安装 IIS Web 服务器。 在“选择角色服务”页上的“管理工具”部分中,选择“IIS 管理脚本和工具”。

  2. 请以管理员身份运行命令。 若要打开提升的命令提示符窗口,请单击“开始”、指向“所有程序”、单击“附件”、右键单击“命令提示符”,然后单击“以管理员身份运行”。 如果你以管理员身份打开命令 shell,则从该命令行管理程序运行的所有应用程序都将以管理员身份运行。

  3. 以文本格式保存脚本文件,用 .vbs 扩展名保存。 可以使用语法“cscript.exe <scriptname.vbs>”在命令提示符下运行这些脚本文件。

  4. 开始前,请使用 AppCmd 工具备份 System32\inetsrv\config\applicationhost.config 文件。 有了备份副本,只需复制原始版本即可将 IIS 还原到其原始状态。 若要进行应用,请执行以下步骤:

    a. 打开提升的命令提示符窗口。
    b. 键入 cd %Windir%\system32\inetsrv\c.Type appcmd add backup backupName 来备份 ApplicationHost.config 文件,其中 backupName 是为备份指定的名称。 将在目录下创建一个具有备份名称的 %Windir%\system32\inetsrv\backup 目录。 如果未指定名称,appcmd 将使用当前日期和时间自动生成目录名称。

工作进程

本部分介绍如何检索 Web 服务器上每个工作进程当前正在执行的请求。 然后,了解如何显示每个工作进程 PID、状态以及所属于的应用程序池。

获取正在执行的请求

IIS 有一项新功能非常吸引人,那就是能够查看当前正在工作进程中执行的请求。 可以使用 WorkerProcess.GetExecutingRequests 方法来查看。

WorkerProcess.GetExecutingRequests 方法以快照方式报告在运行该方法时正在执行的请求。 由于大多数请求的执行速度都非常快,因此可能很难使用 Web 浏览器来手动测试该方法。 要进行测试,你需要创建一个网页。

使用记事本将以下文本粘贴到文本文件中。 然后,将文件另存为 Sleep.aspx。

<%  System.Threading.Thread.Sleep(30000)
Response.Write ("I'm finally finished...") %>

将 Sleep.aspx 文件放在默认网站的内容目录中:%systemdrive%\inetpub\wwwroot

你创建的 Sleep.aspx 文件会强制请求网页执行 30 秒。 这样你就有时间来运行一个脚本,该脚本将显示执行中的 GetExecutingRequests。

GetExecutingRequests 方法采用空白数组变量作为 OUT 参数,然后用 HttpRequest 对象填充该变量。 可以循环访问这些请求以显示每个请求的属性。 以下脚本采用 HttpRequest 对象输出,并显示每个请求的当前模块、谓词、主机名和 URL。

将以下脚本复制到记事本,然后另存为 GetRequests.vbs。

Set oWebAdmin = GetObject("winmgmts:root\WebAdministration")
Set oWorkerProcesses = oWebAdmin.InstancesOf("WorkerProcess")
     
For Each oWorkerProcess In oWorkerProcesses
    ' Place the requests queued for a process into an array variable.
    oWorkerProcess.GetExecutingRequests arrReqs
    
    ' Show the number of requests queued.
    If IsNull(arrReqs) Then
        WScript.Echo "No currently executing requests."

    Else
        ' Display the number of requests.
        WScript.Echo "Number of currently executing requests: " & _
            UBound(arrReqs) + 1
        WScript.Echo
  
        ' List the properties of each request.
        For Each oRequest In arrReqs
            WScript.Echo "Module: " & "[" & oRequest.CurrentModule & "]"
            WScript.Echo "Verb:" & "[" & oRequest.Verb & "]"
            WScript.Echo "HostName: " & "[" & oRequest.HostName & "]"
            WScript.Echo "Url: " & "[" & oRequest.Url & "]"
            WScript.Echo
        Next
    End If
Next

打开提升的命令提示符窗口并导航到保存 GetRequests.vbs 文件的目录。

在运行脚本之前,请在 Web 浏览器的地址栏中键入 http://localhost/sleep.aspx。 这将启动请求执行,并将浏览器设置为在等待呈现 Sleep.aspx 页面的过程中旋转 30 秒。

当浏览器仍在等待呈现页面时,请在刚刚打开的命令提示符窗口中键入以下内容来运行脚本:

Cscript.exe GetRequests.vbs

示例输出

你看到的输出应如下所示。

Number of currently executing requests: 2
Module: [ManagedPipelineHandler]
Verb:[GET]
HostName: [localhost]
Url: [/MyApp/]
Module: [ManagedPipelineHandler]
Verb:[GET]
HostName: [localhost]
Url: [/MyApp/default.aspx]

获取工作进程的状态

IIS WMI 提供程序中的 WorkerProcess 对象具有 GetState 方法,用于显示工作进程是启动、运行还是停止。 WorkerProcess 还有两个属性需要注意:ApplicationPool 和 PID。 ApplicationPool 属性表示工作进程所属的应用程序池。 PID 属性包含用于唯一标识工作进程的进程 ID。

可以使用以下代码列出每个工作进程 PID 和状态及其应用程序池。 如果未运行任何工作进程,脚本将以无提示方式退出。 将代码复制到记事本,并将其另存为 GetState.vbs。

' Connect to the WMI WebAdministration namespace. 
Set oWebAdmin = GetObject("winmgmts:root\WebAdministration") 
       
' Get the worker process instances. 
Set oWorkerProcesses = oWebAdmin.InstancesOf("WorkerProcess") 
       
' Get the ID of each worker process in the application pool and report its status. 
For Each oWorkerProcess In oWorkerProcesses 
       
    ' Report the worker process state via the GetStateDescription helper function. 
    WScript.Echo "WorkerProcess " & oWorkerProcess.ProcessID & ": " & _ 
        GetStateDescription(oWorkerProcess.GetState) 
    WScript.Echo "Application Pool: " & oWorkerProcess.AppPoolName
    WScript.Echo 
Next 

' The helper function translates the return value into text. 
Function GetStateDescription(StateCode) 
    Select Case StateCode 
        Case 0 
            GetStateDescription = "Starting" 
        Case 1 
            GetStateDescription = "Running" 
        Case 2 
            GetStateDescription = "Stopping" 
        Case 3 
            GetStateDescription = "Unknown" 
       
        Case Else 
            GetStateDescription = "Undefined value." 
    End Select 
End Function

打开提升的命令提示符窗口并导航到保存 GetState.vbs 文件的目录。 要运行脚本,可在刚刚打开的命令提示符窗口中键入以下内容:

Cscript.exe GetState.vbs

示例输出

输出应该与下面的内容类似:

WorkerProcess 1336: Running 
Application Pool: DefaultAppPool 
       
WorkerProcess 3680: Running 
Application Pool: Classic .NET AppPool 
       
WorkerProcess 1960: Running 
Application Pool: NewAppPool

现在,你已了解如何使用 WMI 脚本来揭示工作进程的机密,请对应用程序域执行相同的操作。

AppDomain

首次收到对 ASP.NET 页的请求时,IIS 托管引擎模块会在内存中创建应用程序域 (AppDomain)。 AppDomain 处理 aspx 页面的请求,或是处理使用托管代码的任何页面。 使用 WMI 卸载和枚举 AppDomain 非常简单,本部分介绍如何同时执行此操作。

卸载特定 AppDomain

在 IIS 7 及更高版本中卸载 AppDomain 与在 IIS 6.0 中略有不同。 IIS 6.0 AppUnload 命令卸载进程外 ASP 应用程序时,IIS 7 及更高版本的 AppDomain.Unload 方法仅卸载 ASP.NET 应用程序域。 AppUnload 功能已消失,因为 IIS 7 及更高版本中已经不存在该功能支持的 IIS 5.0 兼容模式。

若要卸载特定的 AppDomain,必须能够唯一标识。 AppDomain 对象具有三个关键属性:ApplicationPath、ID 和 SiteName。 但其中任何一个都足以实现你的目的。

顺便说一句,AppDomain ID 属性不是数字,而是如下所示的路径:

/LM/W3SVC/1/ROOT

列出的路径中的“1”是站点 ID(默认情况下,1 对应于默认网站)。如果必须先生成服务器的 AppDomain 及其属性的列表,请参阅本文后面的“枚举 AppDomain”部分。

下一个脚本将卸载名为“Northwind”的 AppDomain。该脚本循环访问可用的 AppDomain,直至找到具有匹配 ApplicationPath 的 AppDomain。 将代码复制到记事本中,将“Northwind”替换为所选的 AppDomain 应用程序路径,并将该文件另存为 AppDomainUnload.vbs。

打开提升的命令提示符窗口并导航到保存 AppDomainUnload.vbs 文件的目录。 要运行脚本,可在刚刚打开的命令提示符窗口中键入以下内容:

Cscript.exe AppDomainUnload.vbs
Set oWebAdmin = GetObject("winmgmts:root\WebAdministration")
Set oAppDomains = oWebAdmin.ExecQuery("SELECT * FROM AppDomain")

' Unload only the Northwind application domain.
For Each oAppDomain In oAppDomains
    If oAppDomain.ApplicationPath = "/Northwind/" Then 
        oAppDomain.Unload
        Exit For 
    End If 
Next

卸载所有 AppDomain

在服务器上卸载所有 AppDomain 更容易:只需检索、循环访问,然后依次卸载。

以下示例卸载 IIS Web 服务器上的所有应用程序域。 请注意,如何使用简单的 WQL 查询(WQL 是 WMI 的 SQL 版本)来检索 AppDomain。

将代码复制到记事本,然后将文件另存为 AppDomainUnloadAll.vbs。 打开提升的命令提示符窗口并导航到保存 AppDomainUnloadAll.vbs 文件的目录。 要运行脚本,可在刚刚打开的命令提示符窗口中键入以下内容:

Cscript.exe AppDomainUnloadAll.vbs
Set oWebAdmin = GetObject("winmgmts:root\WebAdministration")

' Get all the application domains on the Web server.
Set oAppDomains = oWebAdmin.ExecQuery("SELECT * FROM AppDomain")

' Unload all the application domains.
For Each oAppDomain In oAppDomains
    oAppDomain.Unload
Next

作为 WQL 查询语法的替代方法,您还可以使用 WMI InstancesOf 方法,就像之前使用 WorkerProcess 一样:

Set oAppDomains = oWebAdmin.InstancesOf("AppDomain")

枚举 AppDomain

可以使用与前面的脚本类似的方法显示当前运行的所有 AppDomain 及其属性。 下面是 AppDomain 属性的列表:

  • ApplicationPath
  • Id
  • IsIdle
  • PhysicalPath
  • ProcessId
  • SiteName

以下脚本显示每个 AppDomain 的所有属性,但未显示 PhysicalPath 属性,但你可以轻松添加此属性。 为方便起见,脚本单独显示密钥和运行时属性。

将代码复制到记事本,然后将文件另存为 AppDomainProps.vbs。 打开提升的命令提示符窗口并导航到保存 AppDomainProps.vbs 文件的目录。 要运行脚本,可在刚刚打开的命令提示符窗口中键入以下内容:

Cscript.exe AppDomainProps.vbs
'Connect to the WMI WebAdministration namespace
Set oWebAdmin = GetObject("winmgmts:root\WebAdministration")
Set oAppDomains = oWebAdmin.InstancesOf("AppDomain")
WScript.Echo "AppDomain Count: " & oAppDomains.Count
WScript.Echo 
ADCounter = 0
For Each oAppDomain In oAppDomains
    ADCounter = ADCounter + 1
    WScript.Echo "---- AppDomain " & ADCounter & " of " & _
                oAppDomains.Count & " ----" & vbCrLf
    WScript.Echo "[ Key properties ]"
    WScript.Echo "ID: " & oAppDomain.ID
    WScript.Echo "Site Name: " & oAppDomain.SiteName
    WScript.Echo "Application Path: " & oAppDomain.ApplicationPath
    WScript.Echo
    WScript.Echo "[ Run-time properties ]"
    WScript.Echo "Process ID: " & oAppDomain.ProcessID
    WScript.Echo "Is idle: " & oAppDomain.IsIdle
    WScript.Echo vbCrLf
Next

示例输出

您的输出应该如下所示:

AppDomain Count: 3
---- AppDomain 1 of 3 ----
[ Key properties ]
ID: /LM/W3SVC/1/ROOT
Site Name: Default Web Site
Application Path: /

[ Run-time properties ]
Process ID: 3608
Is idle: False

---- AppDomain 2 of 3 ----
[ Key properties ]
ID: /LM/W3SVC/2/ROOT/ContosoApp
Site Name: ContosoSite
Application Path: /ContosoApp/

[ Run-time properties ]
Process ID: 3608
Is idle: True

---- AppDomain 3 of 3 ----
[ Key properties ]
ID: /LM/W3SVC/1/ROOT/Fabrikam
Site Name: Default Web Site
Application Path: /Fabrikam/

[ Run-time properties ]
Process ID: 2552
Is idle: False

结束语

本文介绍了一些基本的 WMI 脚本技术,用于检索有关 IIS 工作进程和 AppDomain 的信息。 WMI InstanceOf 方法和 WQL 查询用于检索它们。 下面简要回顾了呈现的任务以及所使用的方法:

  • 查看当前正在执行的工作进程请求:WorkerProcess.GetExecutingRequests
  • 获取所有工作进程的状态:WorkerProcess.GetState
  • 卸载特定 AppDomain 或所有 AppDomain:AppDomain.Unload