Je viens de découvrir que chaque demande dans une application Web ASP.Net obtient un verrou de session au début d'une demande, puis le libère à la fin de la demande!
Au cas où les implications de cela seraient perdues pour vous, comme c'était le cas pour moi au début, cela signifie essentiellement ce qui suit:
Chaque fois qu'une page Web ASP.Net prend beaucoup de temps à charger (peut-être en raison d'un appel lent à la base de données ou autre), et l'utilisateur décide qu'il souhaite naviguer vers une autre page parce qu'il est fatigué d'attendre, IL NE PEUT PAS! Le verrou de session ASP.Net force la nouvelle demande de page à attendre que la demande d'origine ait terminé son chargement douloureusement lent. Arrrgh.
Chaque fois qu'un UpdatePanel se charge lentement, et l'utilisateur décide de naviguer vers une autre page avant la fin de la mise à jour du UpdatePanel ... IL NE PEUT PAS! Le verrou de session ASP.net force la nouvelle demande de page à attendre que la demande d'origine ait terminé son chargement douloureusement lent. Double Arrrgh!
Quelles sont donc les options? Jusqu'à présent, j'ai trouvé:
- Implémentez un SessionStateDataStore personnalisé, pris en charge par ASP.Net. Je n'en ai pas trouvé trop à copier, et cela semble assez risqué et facile à gâcher.
- Gardez une trace de toutes les demandes en cours et, si une demande provient du même utilisateur, annulez la demande d'origine. Semble un peu extrême, mais cela fonctionnerait (je pense).
- N'utilisez pas Session! Lorsque j'ai besoin d'une sorte d'état pour l'utilisateur, je peux simplement utiliser le cache à la place et les éléments clés sur le nom d'utilisateur authentifié, ou quelque chose de ce genre. Cela semble encore un peu extrême.
Je ne peux vraiment pas croire que l'équipe ASP.Net Microsoft aurait laissé un goulot d'étranglement de performances aussi énorme dans le cadre de la version 4.0! Suis-je en train de manquer quelque chose d'évident? À quel point serait-il difficile d'utiliser une collection ThreadSafe pour la session?
Réponses:
Si votre page ne modifie aucune variable de session, vous pouvez désactiver la plupart de ce verrou.
Si votre page ne lit aucune variable de session, vous pouvez désactiver complètement ce verrou pour cette page.
Si aucune de vos pages n'utilise de variables de session, désactivez simplement l'état de la session dans le web.config.
Je suis curieux, que pensez-vous qu'une "collection ThreadSafe" ferait pour devenir thread-safe, si elle n'utilise pas de verrous?
Edit: je devrais probablement expliquer par ce que je veux dire par "désactiver la plupart de ce verrou". N'importe quel nombre de pages en lecture seule ou sans session peut être traité pour une session donnée en même temps sans se bloquer. Cependant, une page de session en lecture-écriture ne peut pas commencer le traitement tant que toutes les demandes en lecture seule ne sont pas terminées et, pendant son exécution, elle doit avoir un accès exclusif à la session de cet utilisateur afin de maintenir la cohérence. Le verrouillage des valeurs individuelles ne fonctionnerait pas, car que se passe-t-il si une page modifie un ensemble de valeurs associées en tant que groupe? Comment garantiriez-vous que d'autres pages exécutées en même temps obtiennent une vue cohérente des variables de session de l'utilisateur?
Je vous suggère d'essayer de minimiser la modification des variables de session une fois qu'elles ont été définies, si possible. Cela vous permettrait de créer la majorité de vos pages en lecture seule, augmentant ainsi les chances que plusieurs demandes simultanées du même utilisateur ne se bloquent pas.
la source
<pages enableSessionState="ReadOnly" />
dans web.config et utilisez @Page pour activer l'écriture sur des pages spécifiques uniquement.OK, donc gros accessoires à Joel Muller pour toutes ses contributions. Ma solution ultime a été d'utiliser le SessionStateModule personnalisé détaillé à la fin de cet article MSDN:
http://msdn.microsoft.com/en-us/library/system.web.sessionstate.sessionstateutility.aspx
C'était:
Cela a fait une énorme différence dans le sentiment de "snapiness" à notre application. Je n'arrive toujours pas à croire que l'implémentation personnalisée de ASP.Net Session verrouille la session pour toute la demande. Cela ajoute une telle quantité de lenteur aux sites Web. À en juger par la quantité de recherches en ligne que j'ai eu à faire (et les conversations avec plusieurs développeurs ASP.Net vraiment expérimentés), beaucoup de gens ont rencontré ce problème, mais très peu de gens sont allés au fond de la cause. J'écrirai peut-être une lettre à Scott Gu ...
J'espère que cela aide quelques personnes!
la source
ReaderWriterLock
a été déconseillé en faveur deReaderWriterLockSlim
- vous devriez l'utiliser à la place. Deuxièmement,lock (typeof(...))
a également été déconseillé - vous devez plutôt verrouiller sur une instance d'objet statique privé. Troisièmement, l'expression "Cette application n'empêche pas les requêtes Web simultanées d'utiliser le même identifiant de session" est un avertissement, pas une fonctionnalité.SessionStateItemCollection
dans l'exemple de code par une classe thread-safe (peut-être basée surConcurrentDictionary
) si vous voulez éviter les erreurs difficiles à reproduire sous charge.ISessionStateItemCollection
laKeys
propriété doit être de typeSystem.Collections.Specialized.NameObjectCollectionBase.KeysCollection
- qui n'a pas de constructeurs publics. Gee, merci les gars. C'est très pratique.J'ai commencé à utiliser le AngiesList.Redis.RedisSessionStateModule , qui en plus d'utiliser le serveur Redis (très rapide) pour le stockage (j'utilise le port Windows - bien qu'il y ait aussi un port MSOpenTech ), il ne bloque absolument pas la session .
À mon avis, si votre application est structurée de manière raisonnable, ce n'est pas un problème. Si vous avez réellement besoin de données verrouillées et cohérentes dans le cadre de la session, vous devez spécifiquement implémenter vous-même un contrôle de verrouillage / simultané.
À mon avis, décider que chaque session ASP.NET devrait être verrouillée par défaut juste pour gérer une mauvaise conception d'application est une mauvaise décision. Surtout parce qu'il semble que la plupart des développeurs ne réalisent / ne réalisent même pas que les sessions ont été verrouillées, et encore moins que les applications doivent apparemment être structurées afin que vous puissiez faire un état de session en lecture seule autant que possible (opt-out, si possible) .
la source
J'ai préparé une bibliothèque basée sur les liens publiés dans ce fil. Il utilise les exemples de MSDN et CodeProject. Merci à James.
J'ai également apporté des modifications conseillées par Joel Mueller.
Le code est ici:
https://github.com/dermeister0/LockFreeSessionState
Module HashTable:
Module ScaleOut StateServer:
Module personnalisé:
Si vous souhaitez implémenter la prise en charge de Memcached ou Redis, installez ce package. Héritez ensuite la classe LockFreeSessionStateModule et implémentez des méthodes abstraites.
Certains fournisseurs de sessions sans verrouillage utilisant Redis:
la source
Sauf si votre application a des besoins particuliers, je pense que vous avez 2 approches:
La session est non seulement sécurisée pour les threads mais également pour les états, de manière à ce que, jusqu'à ce que la demande en cours soit terminée, chaque variable de session ne changera pas d'une autre demande active. Pour que cela se produise, vous devez vous assurer que la session SERA VERROUILLÉE jusqu'à ce que la demande en cours soit terminée.
Vous pouvez créer une session comme un comportement de plusieurs façons, mais si elle ne verrouille pas la session en cours, elle ne sera pas «session».
Pour les problèmes spécifiques que vous avez mentionnés, je pense que vous devriez vérifier HttpContext.Current.Response.IsClientConnected . Cela peut être utile pour éviter les exécutions et les attentes inutiles sur le client, bien qu'il ne puisse pas résoudre ce problème entièrement, car cela ne peut être utilisé que par une méthode de regroupement et non par async.
la source
Si vous utilisez la mise à jour
Microsoft.Web.RedisSessionStateProvider
(à partir de3.0.2
), vous pouvez l'ajouter à votreweb.config
pour autoriser les sessions simultanées.La source
la source
Pour ASPNET MVC, nous avons fait ce qui suit:
SessionStateBehavior.ReadOnly
toutes les actions du contrôleur en remplaçantDefaultControllerFactory
SessionStateBehavior.Required
Créez ControllerFactory personnalisé et remplacez
GetControllerSessionBehavior
.AcquireSessionLockAttribute
Connectez la fabrique de contrôleurs créée à
global.asax.cs
Maintenant, nous pouvons avoir les deux
read-only
et l'read-write
état de session en un seulController
.la source
Marquer l'état de session d'un contrôleur comme étant en lecture seule ou désactivé résoudra le problème.
Vous pouvez décorer un contrôleur avec l'attribut suivant pour le marquer en lecture seule:
l' énumération System.Web.SessionState.SessionStateBehavior a les valeurs suivantes:
la source
Juste pour aider quiconque avec ce problème (verrouillage des requêtes lors de l'exécution d'un autre depuis la même session) ...
Aujourd'hui, j'ai commencé à résoudre ce problème et, après quelques heures de recherche, je l'ai résolu en supprimant la
Session_Start
méthode (même si elle était vide) du fichier Global.asax .Cela fonctionne dans tous les projets que j'ai testés.
la source
Session_Start
méthode et se verrouille toujoursAprès avoir lutté avec toutes les options disponibles, j'ai fini par écrire un fournisseur SessionStore basé sur un jeton JWT (la session se déplace à l'intérieur d'un cookie, et aucun stockage backend n'est nécessaire).
http://www.drupalonwindows.com/en/content/token-sessionstate
Avantages:
la source