¡Acabo de descubrir que cada solicitud en una aplicación web ASP.Net obtiene un bloqueo de sesión al comienzo de una solicitud, y luego la libera al final de la solicitud!
En caso de que las implicaciones de esto se pierdan para usted, como lo fue para mí al principio, esto básicamente significa lo siguiente:
Cada vez que una página web ASP.Net tarda mucho en cargarse (tal vez debido a una llamada lenta a la base de datos o lo que sea), y el usuario decide que quiere navegar a una página diferente porque está cansado de esperar, ¡NO PUEDEN! El bloqueo de sesión ASP.Net obliga a la nueva solicitud de página a esperar hasta que la solicitud original haya finalizado su carga lenta y dolorosa. Arrrgh
Cada vez que UpdatePanel se carga lentamente, y el usuario decide navegar a una página diferente antes de que UpdatePanel haya terminado de actualizarse ... ¡NO PUEDEN! El bloqueo de sesión de ASP.net obliga a la nueva solicitud de página a esperar hasta que la solicitud original haya finalizado su carga lenta y dolorosa. Doble Arrrgh!
Así que cuales son las opciones? Hasta ahora se me ocurrió:
- Implemente un SessionStateDataStore personalizado, que ASP.Net admite. No he encontrado demasiados para copiar, y parece de alto riesgo y fácil de estropear.
- Realice un seguimiento de todas las solicitudes en curso y, si llega una solicitud del mismo usuario, cancele la solicitud original. Parece un poco extremo, pero funcionaría (creo).
- ¡No uses la sesión! Cuando necesito algún tipo de estado para el usuario, podría usar Cache en su lugar y elementos clave en el nombre de usuario autenticado, o algo así. Nuevamente parece un poco extremo.
¡Realmente no puedo creer que el equipo de ASP.Net Microsoft hubiera dejado un enorme cuello de botella de rendimiento en el marco en la versión 4.0! ¿Me estoy perdiendo algo obvio? ¿Qué tan difícil sería usar una colección ThreadSafe para la sesión?
Respuestas:
Si su página no modifica ninguna variable de sesión, puede inhabilitar la mayor parte de este bloqueo.
Si su página no lee ninguna variable de sesión, puede inhabilitar este bloqueo por completo para esa página.
Si ninguna de sus páginas usa variables de sesión, simplemente desactive el estado de sesión en web.config.
Tengo curiosidad, ¿qué crees que "una colección ThreadSafe" haría para ser seguro para subprocesos, si no usa cerraduras?
Editar: Probablemente debería explicar por lo que quiero decir con "optar por salir de la mayoría de este bloqueo". Se puede procesar cualquier cantidad de páginas de solo lectura o sin sesión para una sesión determinada al mismo tiempo sin bloquearse entre sí. Sin embargo, una página de sesión de lectura-escritura no puede comenzar a procesarse hasta que se hayan completado todas las solicitudes de solo lectura, y mientras se está ejecutando debe tener acceso exclusivo a la sesión de ese usuario para mantener la coherencia. Bloquear valores individuales no funcionaría, porque ¿qué pasa si una página cambia un conjunto de valores relacionados como grupo? ¿Cómo se aseguraría de que otras páginas que se ejecutan al mismo tiempo obtendrían una vista coherente de las variables de sesión del usuario?
Sugeriría que intente minimizar la modificación de las variables de sesión una vez que se hayan configurado, si es posible. Esto le permitiría hacer que la mayoría de sus páginas sean páginas de solo lectura, aumentando la posibilidad de que múltiples solicitudes simultáneas del mismo usuario no se bloqueen entre sí.
fuente
<pages enableSessionState="ReadOnly" />
en web.config y usa @Page para habilitar la escritura solo en páginas específicas.OK, tan grandes apoyos a Joel Muller por toda su aportación. Mi solución final fue usar el Custom SessionStateModule detallado al final de este artículo de MSDN:
http://msdn.microsoft.com/en-us/library/system.web.sessionstate.sessionstateutility.aspx
Esto era:
Esto ha marcado una GRAN diferencia en la sensación de "rapidez" en nuestra aplicación. Todavía no puedo creer que la implementación personalizada de ASP.Net Session bloquee la sesión para toda la solicitud. Esto agrega una enorme cantidad de lentitud a los sitios web. A juzgar por la cantidad de investigación en línea que tuve que hacer (y las conversaciones con varios desarrolladores ASP.Net realmente experimentados), muchas personas han experimentado este problema, pero muy pocas personas han llegado al fondo de la causa. Tal vez le escriba una carta a Scott Gu ...
¡Espero que esto ayude a algunas personas!
fuente
ReaderWriterLock
ha quedado en desuso a favorReaderWriterLockSlim
, debe usarlo en su lugar. En segundo lugar,lock (typeof(...))
también ha quedado en desuso: en su lugar, debe bloquear una instancia de objeto estático privado. En tercer lugar, la frase "Esta aplicación no impide que las solicitudes web simultáneas utilicen el mismo identificador de sesión" es una advertencia, no una función.SessionStateItemCollection
en el código de muestra con una clase segura para subprocesos (quizás basada enConcurrentDictionary
) si desea evitar errores difíciles de reproducir bajo carga.ISessionStateItemCollection
requiere que laKeys
propiedad sea de tipoSystem.Collections.Specialized.NameObjectCollectionBase.KeysCollection
, que no tiene constructores públicos. Gee, gracias chicos. Eso es muy conveniente.Comencé a usar AngiesList.Redis.RedisSessionStateModule , que además de usar el servidor Redis (muy rápido) para el almacenamiento (estoy usando el puerto de Windows , aunque también hay un puerto MSOpenTech ), no bloquea absolutamente la sesión .
En mi opinión, si su aplicación está estructurada de manera razonable, esto no es un problema. Si realmente necesita datos bloqueados y consistentes como parte de la sesión, debe implementar específicamente una verificación de bloqueo / concurrencia por su cuenta.
En mi opinión, la decisión de MS de que cada sesión ASP.NET debería estar bloqueada por defecto solo para manejar un diseño deficiente de la aplicación es una mala decisión. Especialmente porque parece que la mayoría de los desarrolladores no se dieron cuenta / ni siquiera se dieron cuenta de que las sesiones estaban bloqueadas, y mucho menos que las aplicaciones aparentemente necesitan estructurarse para que pueda hacer el estado de sesión de solo lectura tanto como sea posible (optar por salir, cuando sea posible) .
fuente
Preparé una biblioteca basada en enlaces publicados en este hilo. Utiliza los ejemplos de MSDN y CodeProject. Gracias a James
También hice modificaciones aconsejadas por Joel Mueller.
El código está aquí:
https://github.com/dermeister0/LockFreeSessionState
Módulo HashTable:
Módulo ScaleOut StateServer:
Módulo personalizado:
Si desea implementar el soporte de Memcached o Redis, instale este paquete. Luego herede la clase LockFreeSessionStateModule e implemente métodos abstractos.
Algunos proveedores de sesiones sin bloqueo que usan Redis:
fuente
A menos que su aplicación tenga necesidades especiales, creo que tiene 2 enfoques:
La sesión no solo es segura para subprocesos, sino también para el estado, de una manera que sabe que hasta que se complete la solicitud actual, cada variable de sesión no cambiará de otra solicitud activa. Para que esto suceda, debe asegurarse de que la sesión se BLOQUEARÁ hasta que se complete la solicitud actual.
Puede crear una sesión como comportamiento de muchas maneras, pero si no bloquea la sesión actual, no será 'sesión'.
Para los problemas específicos que mencionó, creo que debería consultar HttpContext.Current.Response.IsClientConnected . Esto puede ser útil para evitar ejecuciones innecesarias y esperas en el cliente, aunque no puede resolver este problema por completo, ya que esto solo se puede usar de forma conjunta y no asíncrona.
fuente
Si está utilizando la versión actualizada
Microsoft.Web.RedisSessionStateProvider
(a partir de3.0.2
), puede agregarla a suweb.config
para permitir sesiones concurrentes.Fuente
fuente
Para ASPNET MVC, hicimos lo siguiente:
SessionStateBehavior.ReadOnly
todas las acciones del controlador anulandoDefaultControllerFactory
SessionStateBehavior.Required
Crear ControllerFactory personalizado y anular
GetControllerSessionBehavior
.AcquireSessionLockAttribute
Conecte la fábrica de controladores creada en
global.asax.cs
Ahora, podemos tener ambos estados
read-only
yread-write
estado de sesión en un soloController
.fuente
Marcar el estado de sesión de un controlador como de solo lectura o deshabilitado resolverá el problema.
Puede decorar un controlador con el siguiente atributo para marcarlo como de solo lectura:
la enumeración System.Web.SessionState.SessionStateBehavior tiene los siguientes valores:
fuente
Solo para ayudar a cualquiera con este problema (bloquear solicitudes al ejecutar otra desde la misma sesión) ...
Hoy comencé a resolver este problema y, después de algunas horas de investigación, lo resolví eliminando el
Session_Start
método (incluso si estaba vacío) del archivo Global.asax .Esto funciona en todos los proyectos que he probado.
fuente
Session_Start
método y aún se bloqueaDespués de luchar con todas las opciones disponibles, terminé escribiendo un proveedor de SessionStore basado en token JWT (la sesión viaja dentro de una cookie y no se necesita almacenamiento de back-end).
http://www.drupalonwindows.com/en/content/token-sessionstate
Ventajas:
fuente