Introduction
Dans un environnement sans état comme une application Web, comprendre le concept d'état de session n'a pas de véritable sens. Néanmoins, une gestion efficace des états est une fonctionnalité indispensable pour la plupart des applications Web. Microsoft ASP.NET, ainsi que de nombreux autres environnements de programmation côté serveur, fournissent une couche d'abstraction qui permet aux applications de stocker des données persistantes par utilisateur et par application.
Il est important de noter que l’état de session d’une application Web correspond aux données que l’application met en cache et récupère lors de différentes requêtes. Une session représente toutes les demandes envoyées par l'utilisateur lorsqu'il est connecté au site, et l'état de la session est la collection de données persistantes générées et consommées par l'utilisateur pendant la session. L'état de chaque session est indépendant les uns des autres et cesse d'exister à la fin de la session utilisateur.
L'état de session n'a aucune correspondance avec aucune des entités logiques qui composent le protocole et la spécification HTTP. Les sessions sont une couche d'abstraction construite par des environnements de développement côté serveur tels que ASP traditionnel et ASP.NET. La manière dont ASP.NET affiche l'état de la session et la manière dont l'état de la session est implémenté en interne dépend de l'infrastructure de la plateforme. Par conséquent, ASP traditionnel et ASP.NET implémentent l'état de session de manière complètement différente, et d'autres améliorations et améliorations sont attendues dans la prochaine version d'ASP.NET.
Cet article explique comment implémenter l'état de session dans ASP.NET 1.1 et comment optimiser la gestion de l'état de session dans les applications Web gérées.
Présentation de l'état de session ASP.NET
L'état de session ne fait pas partie de l'infrastructure HTTP. Autrement dit, il devrait y avoir un composant structurel qui lie l'état de session à chaque requête entrante. L'environnement d'exécution (ASP traditionnel ou ASP.NET) peut accepter des mots-clés comme Session et l'utiliser pour indiquer le bloc de données stocké sur le serveur. Pour résoudre avec succès les appels vers un objet Session, l'environnement d'exécution doit ajouter l'état de session au contexte d'appel de la demande en cours de traitement. La manière dont cela est effectué varie selon les plates-formes, mais elle est fondamentale pour les applications Web avec état.
Dans ASP traditionnel, l'état de session est implémenté sous forme d'objets COM à thread libre contenus dans la bibliothèque asp.dll. (Êtes-vous curieux ? Le CLSID de cet objet est en fait D97A6DA0-A865-11cf-83AF-00A0C90C2BD8.) Cet objet stocke les données organisées sous la forme d'une collection de paires nom/valeur. L'espace réservé « nom » représente la clé utilisée pour récupérer les informations, tandis que l'espace réservé « valeur » représente ce qui est stocké dans l'état de session. Les paires nom/valeur sont regroupées par ID de session afin que chaque utilisateur ne voie que les paires nom/valeur qu'il a créées.
Dans ASP.NET, l'interface de programmation pour l'état de session est presque la même que dans ASP traditionnel. Mais leurs implémentations de base sont complètement différentes. La première est plus flexible, évolutive et possède des capacités de programmation plus puissantes que la seconde. Avant d'examiner l'état de la session ASP.NET, passons brièvement en revue certaines des fonctionnalités structurelles de l'infrastructure de session ASP.NET.
Dans ASP.NET, toute requête HTTP entrante est acheminée via le module HTTP. Chaque module peut filtrer et modifier la grande quantité d'informations véhiculées par la requête. Les informations associées à chaque requête sont appelées « contexte d'appel », qui est représenté par l'objet HttpContext en programmation. Nous ne devons pas considérer le contexte de requête comme un autre conteneur d'informations d'état, bien que la collection Items qu'elle fournit ne soit qu'un conteneur de données. L'objet HttpContext diffère de tous les autres objets d'état (par exemple, Session, Application et Cache) en ce sens qu'il a une durée de vie limitée au-delà du temps requis pour traiter la demande. Lorsqu'une requête passe par une série de modules HTTP enregistrés, son objet HttpContext contiendra une référence à l'objet d'état. Lorsque la requête peut enfin être traitée, le contexte d'appel associé est lié à la session spécifique (Session) et aux objets d'état global (Application et Cache).
Le module HTTP responsable de la définition de l'état de session de chaque utilisateur est SessionStateModule. La structure de ce module est conçue sur la base de l'interface IHttpModule, qui fournit un grand nombre de services liés à l'état de session pour les applications ASP.NET. Comprend la génération d'ID de session, la gestion de session sans cookie, la récupération des données de session auprès de fournisseurs d'état externes et la liaison des données au contexte d'appel de la demande.
Le module HTTP ne stocke pas les données de session en interne. L'état de la session est toujours enregistré dans un composant externe appelé « fournisseur d'état ». Le fournisseur d'état encapsule complètement les données d'état de session et communique avec d'autres parties via les méthodes de l'interface IStateClientManager. Le module HTTP d'état de session appelle des méthodes sur cette interface pour lire et enregistrer l'état de session. ASP.NET 1.1 prend en charge trois fournisseurs d'état différents, comme indiqué dans le tableau 1.
Tableau 1 : Statut Client Fournisseur
Fournisseur Description
Les valeurs de session InProc restent des objets actifs dans la mémoire du processus de travail ASP.NET (aspnet_wp.exe ou w3wp.exe dans Microsoft® Windows Server® 2003). Il s'agit de l'option par défaut.
Les valeurs de session StateServer sont sérialisées et stockées en mémoire dans un processus distinct (aspnet_state.exe). Le processus peut également s'exécuter sur d'autres ordinateurs.
Les valeurs de session SQLServer sont sérialisées et stockées dans les tables Microsoft® SQL Server®. Les instances de SQL Server peuvent s'exécuter localement ou à distance.
Le module HTTP d'état de session lit le fournisseur d'état actuellement sélectionné dans la section
Le code ci-dessus accède en fait à la valeur de session créée par le module HTTP dans la mémoire locale. lit les données d'un fournisseur d'État spécifique (voir Figure 1). Que se passe-t-il si d'autres pages tentent également d'accéder à l'état de la session de manière synchrone ? Dans ce cas, la requête en cours peut cesser de traiter des données incohérentes ou périmées. Pour éviter cela, le module d'état de session implémentera un mécanisme de verrouillage lecteur/écrivain et un accès en file d'attente aux valeurs d'état. Les pages avec des autorisations d'écriture sur l'état de session conserveront le verrou d'écriture pour cette session jusqu'à la fin de la demande. Une page peut demander une autorisation d'écriture pour l'état de session en définissant la propriété EnableSessionState de la directive @Page sur true. (Il s'agit du paramètre par défaut). Toutefois, une page peut également avoir un accès en lecture seule à l'état de session, par exemple lorsque la propriété EnableSessionState est définie sur ReadOnly. Dans ce cas, le module conservera le verrouillage du lecteur pour cette session jusqu'à la fin de la demande pour cette page. En conséquence, des lectures simultanées auront lieu. Si une requête de page définit un verrou de lecteur, les autres requêtes simultanées dans la même session ne pourront pas mettre à jour l'état de la session, mais pourront au moins lire. Autrement dit, si une demande en lecture seule est en cours de traitement pour une session, la demande en lecture seule en attente aura une priorité plus élevée qu'une demande nécessitant un accès complet. Si une demande de page définit un verrou d'écriture pour l'état de session, toutes les autres pages seront bloquées, qu'elles souhaitent lire ou écrire du contenu. Par exemple, si deux trames tentent d'écrire dans la session en même temps, une trame doit attendre que l'autre ait terminé avant de pouvoir écrire. Comparaison des fournisseurs d'état Par défaut, les applications ASP.NET stockent l'état de session dans la mémoire d'un processus de travail, en particulier dans un emplacement dédié de l'objet Cache. Lorsque le mode InProc est sélectionné, l'état de la session est stocké dans des emplacements au sein de l'objet Cache. Cet emplacement est marqué comme emplacement privé et n’est pas accessible par programme. En d'autres termes, si vous énumérez tous les éléments du cache de données ASP.NET, aucun objet similaire à l'état de session donné ne sera renvoyé. Les objets cache fournissent deux types d'emplacements : les emplacements privés et les emplacements publics. Les programmeurs peuvent ajouter et gérer des emplacements publics, mais les emplacements privés ne peuvent être utilisés que par le système (en particulier, les classes définies dans la partie system.web). L'état de chaque session active occupe un emplacement dédié dans le cache. L'emplacement est nommé en fonction de l'ID de session et sa valeur est une instance d'une classe interne non déclarée nommée SessionStateItem. Le fournisseur d'état InProc obtient l'ID de session et récupère l'élément correspondant dans le cache. Le contenu de l'objet SessionStateItem est ensuite saisi dans l'objet dictionnaire HttpSessionState et accessible par l'application via la propriété Session. Notez qu'il existe un bogue dans ASP.NET 1.0 qui rend les emplacements privés de l'objet Cache dénombrables par programme. Si vous exécutez le code suivant sous ASP.NET 1.0, vous pourrez énumérer les éléments correspondant aux objets contenus dans chaque état de session actuellement actif. foreach (élément DictionaryEntry dans le cache) Ce bogue a été résolu dans ASP.NET 1.1 et lorsque vous énumérez le contenu mis en cache, aucun emplacement système ne sera plus répertorié. InProc est probablement de loin l’option d’accès la plus rapide. Mais gardez à l’esprit que plus les données sont stockées dans une session, plus le serveur Web consomme de la mémoire, ce qui augmente potentiellement le risque de dégradation des performances. Si vous envisagez d'utiliser une solution hors processus, vous devez examiner attentivement les effets possibles de la sérialisation et de la désérialisation. La solution hors processus utilise un service Windows NT (aspnet_state.exe) ou une table SQL Server pour stocker les valeurs de session. Par conséquent, l’état de session reste en dehors du processus de travail ASP.NET et des couches de code supplémentaires sont nécessaires pour sérialiser et désérialiser entre l’état de session et le support de stockage réel. Cela se produit chaque fois qu'une demande est traitée et doit ensuite être optimisée au maximum. Étant donné que les données de session doivent être copiées du référentiel externe vers le dictionnaire de session local, la requête entraîne une dégradation des performances allant de 15 % (hors processus) à 25 % (SQL Server). Notez que même s’il ne s’agit que d’une estimation approximative, elle devrait être proche de l’impact minimum et l’impact maximum sera bien supérieur à cela. En fait, cette estimation ne prend pas pleinement en compte la complexité des types réellement sauvegardés dans l’état de session. Dans le scénario de stockage hors processus, l'état de session survit plus longtemps, ce qui rend l'application plus puissante car elle protège contre les pannes de Microsoft® Internet Information Services (IIS) et d'ASP.NET. En séparant l'état de session des applications, vous pouvez également étendre plus facilement les applications existantes dans les architectures Web Farm et Web Garden. De plus, l'état de la session est stocké dans un processus externe, éliminant ainsi le risque de perte de données périodique due aux boucles de processus. Voici comment utiliser les services Windows NT. Comme mentionné ci-dessus, le service NT est un processus nommé aspnet_state.exe, généralement situé dans le dossier C:WINNTMicrosoft.NETFrameworkv1.1.4322. Le répertoire réel dépend de la version de Microsoft® .NET Framework que vous exécutez réellement. Avant d'utiliser le serveur d'état, vous devez vous assurer que le service est prêt et exécuté sur l'ordinateur local ou distant utilisé comme périphérique de stockage de session. Le service d'état fait partie et est installé avec ASP.NET, vous n'avez donc pas besoin d'exécuter un programme d'installation supplémentaire. Par défaut, le service d'état n'est pas en cours d'exécution et doit être démarré manuellement. L'application ASP.NET tentera d'établir une connexion au serveur d'état immédiatement après son chargement. Par conséquent, le service doit être prêt et exécuté, sinon une exception HTTP sera levée. L'image suivante montre la boîte de dialogue des propriétés du service. Les applications ASP.NET doivent spécifier l'adresse TCP/IP de l'ordinateur sur lequel se trouve le service d'état de session. Les paramètres suivants doivent être saisis dans le fichier web.config de l'application. <configuration>; L'attribut stateConnectionString contient l'adresse IP de l'ordinateur et le port utilisé pour l'échange de données. L'adresse par défaut de l'ordinateur est 127.0.0.1 (localhost) et le port par défaut est 42424. Vous pouvez également indiquer l'ordinateur par son nom. L'utilisation d'un ordinateur local ou distant est totalement transparente pour le code. Notez que les caractères non-ASCII ne peuvent pas être utilisés dans le nom et que le numéro de port est obligatoire. Si vous utilisez le stockage de session hors processus, l'état de session existera toujours et sera disponible pour une utilisation future, quel que soit ce qui arrive au processus de travail ASP.NET. Si le service est interrompu, les données seront conservées et automatiquement récupérées lors de la restauration du service. Cependant, si le service du fournisseur de statut s'arrête ou échoue, les données seront perdues. Si vous souhaitez que votre application soit puissante, utilisez le mode SQLServer au lieu du mode StateServer. <configuration>; Vous pouvez spécifier la chaîne de connexion via l'attribut sqlConnectionString. Notez que la chaîne d'attribut doit contenir l'ID utilisateur, le mot de passe et le nom du serveur. Il ne peut pas contenir de balises telles que Base de données et Catalogue initial, car ces informations portent par défaut un nom fixe. Les identifiants utilisateur et les mots de passe peuvent être remplacés par des paramètres de sécurité intégrés. Comment créer une base de données ? ASP.NET fournit deux paires de scripts pour configurer l'environnement de base de données. La première paire de scripts est nommée InstallSqlState.sql et UninstallSqlState.sql et se trouve dans le même dossier que le service Session State NT. Ils créent une base de données nommée ASPState et plusieurs procédures stockées. Cependant, les données sont stockées dans la base de données TempDB de la zone de stockage temporaire SQL Server. Cela signifie que si l'ordinateur SQL Server est redémarré, les données de session seront perdues. Pour contourner cette limitation, utilisez une deuxième paire de scripts. La deuxième paire de scripts est nommée InstallPersistSqlState.sql et UninstallPersistSqlState.sql. Dans ce cas, la base de données ASPState est créée, mais les tables sont créées dans la même base de données et sont également persistantes. Lorsque vous installez la prise en charge de SQL Server pour les sessions, une tâche est également créée pour supprimer les sessions expirées dans la base de données d'état de session. Le travail est nommé ASPState_Job_DeleteExpiredSessions et est toujours en cours d'exécution. Veuillez noter que pour que cette tâche fonctionne correctement, le service SQLServerAgent doit être en cours d'exécution. Quel que soit le mode que vous choisissez, la façon dont les opérations sur l’état de session sont codées ne change pas. Vous pouvez toujours travailler sur la propriété Session et lire et écrire des valeurs normalement. Toutes les différences de comportement sont traitées à un niveau d'abstraction inférieur. La sérialisation d'état est peut-être la différence la plus importante entre les modes de session. Sérialisation et désérialisation d'état Lors de l'utilisation du mode en cours, les objets sont stockés dans l'état de session en tant qu'instances actives de leurs classes respectives. Si aucune sérialisation ni désérialisation réelle ne se produit, cela signifie que vous pouvez réellement stocker n'importe quel objet que vous créez dans la session (y compris les objets qui ne peuvent pas être sérialisés et les objets COM), et y accéder ne sera pas trop coûteux. Si vous choisissez un prestataire étatique hors processus, c'est une autre histoire. Dans une architecture hors processus, les valeurs de session sont copiées du support de stockage local (base de données AppDomain externe) vers la mémoire de l'AppDomain qui gère la requête. Une couche de sérialisation/désérialisation est nécessaire pour accomplir cette tâche et représente l'un des coûts majeurs des fournisseurs d'état hors processus. Le principal impact de cette situation sur votre code est que seuls les objets sérialisables peuvent être stockés dans le dictionnaire de session. ASP.NET utilise deux méthodes pour sérialiser et désérialiser les données, selon le type de données impliqué. Pour les types de base, ASP.NET utilise un sérialiseur interne optimisé ; pour les autres types, notamment les objets et les classes définies par l'utilisateur, ASP.NET utilise le formateur binaire .NET. Les types de base incluent les chaînes, les dates et heures, les valeurs booléennes, les octets, les caractères et tous les types numériques. Pour ces types, l’utilisation d’un sérialiseur sur mesure est plus rapide que l’utilisation du formateur binaire .NET commun par défaut. Le sérialiseur optimisé n’est ni publié ni documenté publiquement. Il s'agit simplement d'un lecteur/enregistreur binaire et utilise une architecture de stockage simple mais efficace. Le sérialiseur utilise la classe BinaryWriter pour écrire une représentation en octets du type, puis écrit une représentation en octets de la valeur correspondant à ce type. Lors de la lecture d'octets sérialisés, la classe extrait d'abord un octet, détecte le type de données à lire, puis appelle la méthode ReadXxx spécifique au type sur la classe BinaryReader. Notez que la taille des types booléens et numériques est bien connue, mais pas pour les chaînes. Sur le flux de données sous-jacent, la chaîne est toujours préfixée par une longueur fixe (un code entier de 7 bits écrit à la fois), et le lecteur utilise ce fait pour déterminer la taille correcte de la chaîne. La valeur de la date est enregistrée en écrivant uniquement le nombre total de jetons qui composent la date. Par conséquent, pour sérialiser la session, la date doit être de type Int64. Vous pouvez utiliser la classe BinaryFormatter pour effectuer des opérations de sérialisation sur des objets plus complexes (ainsi que sur des objets personnalisés) à condition que la classe conteneur soit marquée comme sérialisable. Tous les types non basiques sont identifiés par le même ID de type et stockés dans le même flux de données que les types de base. Dans l'ensemble, les opérations de sérialisation peuvent entraîner une dégradation des performances de 15 à 25 %. Notez cependant qu'il s'agit d'une estimation approximative basée sur l'hypothèse que des types de base sont utilisés. Plus les types utilisés sont complexes, plus la surcharge est importante. Un stockage efficace des données de session est difficile à mettre en œuvre sans une utilisation intensive de types primitifs. Ainsi, au moins en théorie, il est préférable d'utiliser trois emplacements de session pour enregistrer trois propriétés de chaîne différentes d'un objet plutôt que de sérialiser l'objet entier. Mais que se passe-t-il si l’objet que vous souhaitez sérialiser contient 100 propriétés ? Voulez-vous utiliser 100 emplacements ou un seul emplacement ? Dans de nombreux cas, une meilleure approche consiste à convertir un type complexe en plusieurs types plus simples. Cette approche est basée sur des convertisseurs de types. Un « convertisseur de type » est un sérialiseur léger qui renvoie les propriétés clés d'un type sous la forme d'une collection de chaînes. Les convertisseurs de types sont des classes externes liées à une classe de base à l'aide d'attributs. C'est au rédacteur de écrire de décider quelles propriétés sont enregistrées et comment. Les convertisseurs de type sont également utiles pour le stockage ViewState et représentent une méthode de stockage de session plus efficace que les formateurs binaires. Cycle de vie de session Un point important concernant la gestion de session ASP.NET est que le cycle de vie de l'objet d'état de session commence uniquement lorsque le premier élément est ajouté au dictionnaire en mémoire. Une session ASP.NET est considérée comme démarrée uniquement après l'exécution de l'extrait de code suivant. Session["MySlot"] = "Certaines données"; Le dictionnaire Session contient généralement le type Object Pour lire les données à l'envers, la valeur renvoyée doit être convertie en un type plus spécifique. string data = (string) Session["MySlot"]; Lorsque la page enregistre les données dans la session, la valeur sera chargée dans la classe de dictionnaire spécialement conçue contenue dans la classe HttpSessionState. Le contenu du dictionnaire est chargé dans le fournisseur d'état lorsque la requête en cours de traitement est terminée. Si l'état de la session est vide parce que les données n'ont pas été placées dans le dictionnaire par programme, les données ne seront pas sérialisées sur le support de stockage et, plus important encore, ne seront pas servies dans le cache ASP.NET, SQL Server ou NT State Services. un emplacement pour suivre la session en cours. Ceci est pour des raisons de performances, mais cela a un impact important sur la façon dont les ID de session sont gérés : un nouvel ID de session sera généré pour chaque requête jusqu'à ce que certaines données soient stockées dans le dictionnaire de session. Lorsqu'il est nécessaire de connecter l'état de session à une requête en cours de traitement, le module HTTP récupère l'ID de session (s'il ne s'agit pas de la requête initiatrice) et le recherche dans le fournisseur d'état configuré. Si aucune donnée n'est renvoyée, le module HTTP génère un nouvel ID de session pour la requête. Cela peut être facilement testé avec la page suivante : <%@ Page Language="C#" Trace="true" %>;
{
Response.Write(elem.Key + ": " + elem.Value.ToString());
}
Figure 2 : boîte de dialogue Propriétés du serveur d'état ASP.NET
<état de session
mode = "Serveur d'état"
stateConnectionString="tcpip=expoware:42424" />;
</system.web>;
;
<étatdesession
mode="SQLServeur"
sqlConnectionString="server=127.0.0.1;uid=<id utilisateur>;;pwd=<mot de passe>;;" />;
</system.web>;
;
</html>;