On parle souvent de recyclage de processus IIS mais ASP.NET permet aussi de recycler un Application Domain ou AppDomain. Pour ceux qui découvrent cette notion, on peut définir un AppDomain comme un processus .NET hébergé dans un processus Windows classique (dans le cas d'IIS/ASP.NET: W3WP.EXE). Un pool d'applications IIS peut ainsi héberger, dans un même espace mémoire, plusieurs application .NET, chaque application tournant dans son propre AppDomain. Ceci permet notamment à deux applications .NET hébergées dans un même worker process IIS (W3WP) de communiquer plus rapidement sans avoir besoin d'utiliser une communication inter processus complexe (RPCs, canaux nommés…etc).

WinDbg et l'extention SoS permet de visualiser les AppDomains chargés dans un processus à l'aide de la commande !DumpDomain. Dans l'exemple ci-dessous, on peut ainsi identifier que le global.asax est chargé dans l'AppDomain2 :

!DumpDomain

--------------------------------------
System Domain: 6d9ae1f8
LowFrequencyHeap: 6d9ae21c
HighFrequencyHeap: 6d9ae268
StubHeap: 6d9ae2b4
Stage: OPEN
Name: None
--------------------------------------
Shared Domain: 6d9adb48
LowFrequencyHeap: 6d9adb6c
HighFrequencyHeap: 6d9adbb8
StubHeap: 6d9adc04
Stage: OPEN
Name: None
Assembly: 015e6640
[…]
Assembly: 015e6020
--------------------------------------
Domain 1: 001107f0
LowFrequencyHeap: 00110814
HighFrequencyHeap: 00110860
StubHeap: 001108ac
Stage: OPEN
SecurityDescriptor: 015b9920
Name: DefaultDomain
Assembly: 001b2fa0
[D:\Windows\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll]
ClassLoader: 001b3010
SecurityDescriptor: 001f0dc0
Module Name
[…]
--------------------------------------
Domain 2: 015f99f0
LowFrequencyHeap: 015f9a14
HighFrequencyHeap: 015f9a60
StubHeap: 015f9aac
Stage: OPEN
SecurityDescriptor: 015b9558
Name: /LM/W3SVC/5/ROOT-1-129055248675466786
Assembly: 001b2fa0
[D:\Windows\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll]
ClassLoader: 001b3010
SecurityDescriptor: 015ea7f8
Module Name
60c11000 D:\Windows\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll
007f2358 D:\Windows\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\sortkey.nlp
007f2010 D:\Windows\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\sorttbls.nlp

Assembly: 015e5f40 [D:\Windows\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files\root\6d9924ec\86afc5bf\App_global.asax.siihhkue.dll]
ClassLoader: 015e5fb0
SecurityDescriptor: 015eb078
Module Name
00ab55c0 D:\Windows\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files\root\6d9924ec\86afc5bf\App_global.asax.siihhkue.dll


Cette fonctionnalité existait déjà dans les versions précédentes d'IIS mais, une nouveauté d'IIS7 est de permettre le recyclage d'un AppDomain à la demande. Vous trouvez ci-dessous un script d'exemple vous permettant d'énumérer et de décharger un AppDomain à la demande via la méthode WMI AppDomain.Unload :

UnloadAppDomainSample.vbs

'Author: Julien Clauzel
'Description: Sample script to unload appdomains on IIS7
'Input:
'    If no parameters are provided list appdomain and prompt for recycling
'    If a parameter is provided it is assumed to be an appdomain name to be recycled. For example MyAppDomain

'Output: set %errorlevel% variable to :
'    0 if successful,
'    -1 if appdomain passed as a parameter is not found
'    -2 if some required IIS components are not found or failed to initialize
'    value of the error

Option Explicit
Dim oService, Input, oAppDom, AppDom, oArgs, bAppDomainFound

Sub Cleanup
    Set oService = Nothing
    Set oAppDom = Nothing
    Set oArg = Nothing
End Sub

Sub UnloadAppDomain (AppDomain)
    AppDomain.Unload
    If Err.Number <> 0 Then
        WScript.Echo " =>ERROR while unloading:" & Err.Number & " " & Err.Description        Cleanup
        WScript.Quit Err.Number
    Else
        WScript.Echo " =>Unloaded"
        If oArgs.Count = 1 Then bAppDomainFound = True
    End If
End Sub

bAppDomainFound = False
Input = ""

On Error Resume next

Set oService = GetObject("winmgmts:root\WebAdministration")
Set oArgs = WScript.Arguments

' Get the AppDomains
Set oAppDom = oService.InstancesOf("AppDomain")

If Err.Number <> 0 Then
    WScript.Echo "Error: some IIS compontents are not found or failed to initialize, exiting."
WScript.Quit -2
End If

For Each AppDom In oAppDom
If Err.Number = 0 Then
    If oArgs.Count = 1 Then
        If AppDom.SiteName = oArgs(0) Then
            UnloadAppDomain (AppDom)
        End If
    Else
         WScript.Echo " AppDomain => " & AppDom.SiteName & " PID:" & AppDom.ProcessId
        WScript.Echo " Unload AppDomain (Y/N)?"
        Input = WScript.StdIn.ReadLine
         If Input ="Y" then
            UnloadAppDomain (AppDom)
         Else
            WScript.Echo " =>Skipped."
            WScript.Echo ""
         End If
    End If
End If
Next

If oArgs.Count = 1 and Not bAppDomainFound Then
        WScript.Echo "Error AppDomain:" & oArgs(0) & " not found"
        Cleanup
        WScript.Quit -1
    End If
Cleanup
WScript.Echo "Exiting"
WScript.Quit 0


Exemple d'utilisation :

UnloadAppDomain.vbs

AppDomain => JulianGroove PID:9596
Unload AppDomain (Y/N)?
Y
=>Unloaded
AppDomain => WebNot PID:6188
Unload AppDomain (Y/N)?
N
=>Skipped.
Exiting


Le script permet aussi de décharger un AppDomain en passant simplement son nom en argument :

UnloadAppDomain.vbs WebNotification

=>Unloaded
Exiting


Quelques remarques :

  • Quand le script termine avec succès la variable %errorlevel% est positionné à 0
  • Si le nom de l'AppDomain passé en paramètre ne peut être trouvé, la valeur retournée est : -1
  • Si un des composants nécessaire au script n'est pas trouvé, la valeur retournée est : -2
  • Si une erreur est rencontrée, %errorlevel% est positionné à la valeur de l'erreur

On peut se demander quand un processus ou un AppDomain doit être recyclé et quels sont les impacts du recyclage.

Le recyclage d'un processus IIS est conditionné par le paramétrage effectué dans le gestionnaire IIS :

  • Le système ou l'administrateur a défini une politique de recyclage (par défaut toutes les 1740 minutes à compter du démarrage du processus)

  • Un recyclage manuel a été demandé soit :
    • Via le gestionnaire IIS
    • En ligne de commande (voir notre blog sur le sujet) :
      • appcmd list wp
        • Cette commande donne la liste des pools d'application chargé en mémoire
      • appcmd recycle apppool DefaultAppPool
        • Recycle le DefaultAppPool

Quand un processus est recyclé, l'ancien processus s'arrête et un nouveau processus prend la relève pour continuer à traiter les requêtes des utilisateurs. Dans ce cas, les caches ASP.NET sont entièrement réinitialisés et les sessions des utilisateurs sont perdues si elles n'ont pas été externalisées.

Un AppDomain peut quant à lui être re-démarrer pour différentes raisons (liste non exhaustive) :

  • Modification du web.config (altération manuelle du fichier ou par un antivirus par exemple – voir cet article http://support.microsoft.com/kb/316148)
  • En mode d'application pool intégré, si la configuration impactant ce pool est modifiée.
  • Si la valeur numRecompilesBeforeApprestart http://msdn.microsoft.com/en-us/library/s10awwz0(VS.71).aspx a été atteinte.
  • Par l'administrateur ou le développeur à l'aide du script ci-dessus.

Lors de l'arrêt et du redémarrage d'un AppDomain, les caches ASP.NET sont également purgés, les sessions utilisateurs sont perdues (sauf si elles ont été persistés à l'extérieur du processus). Les modules sont déchargés. Cependant, vous pouvez trouver encore une « trace » de leur présence passée à l'aide de la commande « lm » en vous attachant au processus à l'aide de WinDbg.

Par exemple :    

Lm

Unloaded modules:

70370000 7037a000 App_global.asax.1entu8ls.dll


Au niveau de l'utilisation mémoire, l'arrêt d'un AppDomain aura un impact plus limité qu'un recyclage complet du processus W3WP. Vous devriez cependant constater un gain significatif au niveau de la heap managée (vous pouvez éventuellement utiliser la commande !dumpheap -stat et examiner les objets de type « Free » pour mesurer ce gain).

En synthèse le recyclage d'un processus W3WP permet de « redémarrer » complètement un processus au niveau mémoire alors que le recyclage d'un AppDomain quant à lui peu en garder des traces. Ce qui est toutefois important à souligner est que les recyclages d'AppDomain peuvent être réalisés de manière automatique ou préventive par le système ou l'administrateur. Pour identifier si des recyclages d'AppDomain interviennent dans votre environnement, utilisez le compteur de performance ASP.NET v2.0.5727\Application Restarts. Pour identifier si des recyclages de processus surviennent activer la variable LogEventOnRecycle à 255 (reportez vous à cet article pour de plus amples informations).

Votre application peut être amenée à recycler à n'importe quel moment, que ce soit en heure creuse ou en pic de charge. Par conséquent, lors de votre phase de design et dans vos tests de charge, prenez en compte cet élément important et réalisez des recyclages d'AppDomain et/ou des processus W3WP. Nous recommandons généralement que toutes les applications soient conçues afin de pouvoir continuer à fournir le même service aux utilisateurs alors que ces actions sont en cours de réalisation. Vous pouvez vous aider du script ci-dessus ainsi que de APPCMD pour générer ces recyclages.

Julien Clauzel