Problemas con ASP.NET Session en Web Farm

203 views
Skip to first unread message

Carlos Admirador

unread,
Oct 23, 2015, 2:58:10 PM10/23/15
to AltNet-Hispano
Hola,

Tengo la siguiente problemática, en una aplicación WebForms ASP.NET 4.5.1 en IIS 7.5, servidores Windows Server 2008 R2.

Con Balanceador F5, con DSN con nombre virtual : https://loadbalancer.hostname.com/v2

con 3 nodos físicos

http://serverPRODUCCION01:49100/v2

http://serverPRODUCCION02:49100/v2

http://serverPRODUCCION03:49100/v2


Sigue un esquema WebFarm
http://www.codeproject.com/Articles/114910/What-is-the-difference-between-Web-Farm-and-Web-Ga

Session State en Web Farm, el machine.key es el mismo en los 3 servidores
https://accidentaltechnologist.com/microsoft/how-to-configure-session-state-in-a-windows-web-farm/

Configuración cookieless="false", The session ID is stored in an HTTP cookie.
https://msdn.microsoft.com/en-us/library/system.web.sessionstate.httpsessionstate.sessionid.aspx

Hemos detectado que tras el despliegue en PRODUCCION, en los 3 nodos (se ha desplegado mismas dlls, y web.config en los 3) aparecen errroes relativos a la sessión.

Por ejemplo, accediendo con la url  https://loadbalancer.hostname.com/v2  cargaba un control de la Home (Default.aspx) y salía con datos pero al refrescar (pulsar tecla F5) ya los mostraba y luego volvía a refrescar (F5) entonces no los veía.

Accediendo con el mismo usuario y con url http://serverPRODUCCION01:49100/v2 y http://serverPRODUCCION03:49100/v2 no se mostraban

Accediendo con el mismo usuario y con url http://serverPRODUCCION02:49100/v2 se mostraban


                     Alguna buena p´ractica para hacer troubleshooting respecto a Sesiones (SessionID, ...) ?

1. ) accediendo con el mismo usuario X con la url  http://loadbalancer.hostname.com/v2 en varios navegadores y/o pestañas IE , el valor de SessionID será el mismo ?

2. ) accediendo con el mismo usuario X con las urls 

http://serverPRODUCCION01:49100/v2

http://serverPRODUCCION02:49100/v2

http://serverPRODUCCION03:49100/v2

en varios navegadores y/o pestañas IE , el valor de SessionID será el mismo ?

Ideas en mente que tengo, quizá un troubleshooting así:

// In a user control or page
string sessionId = this.Session.SessionID; 

// In a normal class, running in a asp.net app.
string sessionId = System.Web.HttpContext.Current.Session.SessionID;


De esto no entiendo nada ( por mi ignorancia):
http://stackoverflow.com/questions/2874078/asp-net-session-sessionid-changes-between-requests

http://stackoverflow.com/questions/746475/get-session-id-in-asp-net

https://konste.wordpress.com/2013/01/22/asp-net-accessing-any-session-data-by-session-id/


According to Dino Esposito each session is stored in the application's Cache and with some work you can retreive this information:

DataTable dt = new DataTable();
dt.Columns.Add("SessionID", typeof(string));
foreach(DictionaryEntry elem in Cache) {
    string s = elem.Key.ToString();
    if (s.StartsWith("System.Web.SessionState.SessionStateItem")) {
    	DataRow row = dt.NewRow();
    	char[] parms = {':'};
    	string[] a = s.Split(parms);
    	row["SessionID"] = a[1];
    	dt.Rows.Add(row);
    }
}

In the Web.config, if there is an <httpCookies> entry that is set to requireSSL="true" but you are not actually using HTTPS: for a specific request, then the session cookie is not sent (or maybe not returned, I'm not sure which) which means that you end up with a brand new session for each request.



Cualquier comentario les agradecería muchísimo.

Muchas gracias comunidad

Modesto San Juan

unread,
Oct 24, 2015, 11:35:10 AM10/24/15
to altnet-...@googlegroups.com
Hola,

¿Has verificado que la sesión no se está gestionando In-Proc?


Por otro lado, esto tal vez esto te pueda ayudar a diagnosticar el problema.:



Un saludo

--
Has recibido este mensaje porque estás suscrito al grupo "AltNet-Hispano" de Grupos de Google.
Para anular la suscripción a este grupo y dejar de recibir sus mensajes, envía un correo electrónico a altnet-hispan...@googlegroups.com.
Para publicar en este grupo, envía un correo electrónico a altnet-...@googlegroups.com.
Visita este grupo en http://groups.google.com/group/altnet-hispano.
Para acceder a más opciones, visita https://groups.google.com/d/optout.

José Manuel Corral

unread,
Oct 25, 2015, 7:19:32 PM10/25/15
to altnet-...@googlegroups.com

Buenas,

   Para webfarming necesitas almacenar la session o en un IIS ha de ser el mismo para los 3 servidores, o en una base de datos SQL. Estas perdiendo sesion, por que almacenas la cookie que contiene el sessionid, pero el server al ir a buscar los datos seriados en la session no los encuentra, ya que estan en la memoria de otro server. No se si me he explicado con claridad, cualquier aclaración me dices.

Saludos.

juan ladetto

unread,
Oct 26, 2015, 8:36:59 AM10/26/15
to altnet-...@googlegroups.com
Hola...
Cómo dicen arriba el problema es el session state provider. El que viene por defecto es in proc y los valores no se comparten entre un farm.
Recomiendo leer esto para implementar un custom session state provider.

Carlos Admirador

unread,
Oct 26, 2015, 3:21:16 PM10/26/15
to AltNet-Hispano

Tengo esta configuración mode="SQLServer"


<sessionState mode="SQLServer"
              sqlConnectionString="ConnectionStrings.SqlServer.ProveedorPerfil"
              allowCustomSqlDatabase="true"
              timeout="60"
              cookieName="s"
              compressionEnabled="true" />
 


<siteMap defaultProvider="SqlSiteMapProvider">
  <providers>
    <clear />
    <add name="SqlSiteMapProvider"
         type="WebIU.AppCode.siteMapProviders.SqlSiteMapProvider"
         securityTrimmingEnabled="true"
         EsCargaArbolRoles="false"
         RolesCampanasCliente="MA,MG,ME,MC"
         RolesCampanas2="PR,AR,AgeC2" />

    <
add name="SqlCargaArbolSiteMapProvider"
         type="WebIU.AppCode.siteMapProviders.CMS.SqlSiteMapProvider"
         securityTrimmingEnabled="true"
         EsCargaArbolRoles="true" />
  </providers>
</siteMap>
 
 
<roleManager enabled="true"
             cacheRolesInCookie="true"
             cookieName=".ASPROLES"
             cookieTimeout="30"
             cookieRequireSSL="false"
             cookieSlidingExpiration="true"
             cookieProtection="All"
             defaultProvider="MyRoleProvider"
             maxCachedResults="1000">
  <providers>
    <clear />
    <add name="MyRoleProvider" connectionStringName="ConnectionStrings.Oracle.C1" applicationName="MyApp"
         type="Seguridad.Bll.MyRoleProvider, Seguridad" />
  </providers>
</roleManager
 
 
<profile defaultProvider="SqlProfileProvider">
  <providers>
    <clear />
    <add name="SqlProfileProvider" type="System.Web.Profile.SqlProfileProvider" connectionStringName="MySqlServer" applicationName="MyApp" />
  </providers>
  <properties>
    <add name="Perfil" type="Personalizacion.Entidades.Perfil" serializeAs="Binary" />
    <add name="Desktop" type="Common.Logic.Bll.Desktop, Common, Version=4.0.0.0, Culture=neutral, PublicKeyToken=zzz" />
  </properties>
</profile>
 
Y la clase SiteMap

namespace WebIU.AppCode.siteMapProviders.CMS

{

 

    [SqlClientPermission(SecurityAction.Demand, Unrestricted = true)]

    public class SqlSiteMapProvider : StaticSiteMapProvider

    {

        private Dictionary<int, SiteMapNode> _nodes = new Dictionary<int, SiteMapNode>(16);

        private readonly object _lock = new object();

        private SiteMapNode _root;

        private bool _EsCargaArbol = false;

        private string _rolesIniciales = "";

        private List<string> _rolesCampanasClientes = new List<string>();

        private List<string> _rolesCampanasMediador = new List<string>();

 

        public void Refresh()

        {

            Clear();

            _nodes.Clear();

            _root = null;

        }

 

        public override void Initialize(string name, NameValueCollection config)

        {

            // Verify that config isn't null

            if (config == null)

                throw new ArgumentNullException("config");

 

            if (config.AllKeys.Contains("EsCargaArbolRoles"))

                _EsCargaArbol = Convert.ToBoolean(config.Get("EsCargaArbolRoles"));

 

            //clave para cargar el sitemap en base a unos roles

            if (config.AllKeys.Contains("RolesIniciales"))

                _rolesIniciales = config.Get("RolesIniciales");

 

 

            // Assign the provider a default name if it doesn't have one

            if (String.IsNullOrEmpty(name))

                name = "SqlSiteMapProvider";

 

            // Add a default "description" attribute to config if the

            // attribute doesn’t exist or is empty

            if (string.IsNullOrEmpty(config["description"]))

            {

                config.Remove("description");

                config.Add("description", "SQL site map provider");

            }

 

            if (config.AllKeys.Contains("RolesCampanasCliente"))

            {

                foreach (var rol in config.Get("RolesCampanasCliente").Split(new char[] { ',' }))

                {

                    _rolesCampanasClientes.Add(rol);

                }

            }

 

            if (config.AllKeys.Contains("RolesCampanas2"))

            {

                foreach (var rol in config.Get("RolesCampanas2").Split(new char[] { ',' }))

                {

                    _rolesCampanasMediador.Add(rol);

                }

            }

 

            // Call the base class's Initialize method

            base.Initialize(name, config);

 

            // SiteMapProvider processes the securityTrimmingEnabled

            // attribute but fails to remove it. Remove it now so we can

            // check for unrecognized configuration attributes.

 

            if (config["securityTrimmingEnabled"] != null)

                config.Remove("securityTrimmingEnabled");

 

        }

 
       public override SiteMapNode BuildSiteMap()

        {

            lock (_lock)

            {

                // Return immediately if this method has been called before

                if (_root != null) return _root;

 

                var listado = Seguridad.Dal.NodoSiteMap.DevolverTodosNodosSiteMap();

                if (listado.Count == 0) return _root;

 

                // Create the root SiteMapNode and add it to the site map

                _root = CreateSiteMapNodeFromDataReader(listado[0]);

                AddNode(_root, null);

                //eliminamos el nodo introducido

                listado.Remove(listado[0]);

 

                // Build a tree of SiteMapNodes underneath the root node

                foreach (var reader in listado)

                {

                    var node = CreateSiteMapNodeFromDataReader(reader);

                        AddNode(node, GetParentNodeFromDataReader(reader));

 

                    if (_EsCargaArbol) continue;

                    //comprobamos si tiene que crearse mas nodos(campañas, noticias, etc...), esto vendrá indicado por el provider que contenga en la BBDD

                    if (reader.NameProvider == "") continue;

 

                }

 

                return _root;

            }

        }

 

        protected override SiteMapNode GetRootNodeCore()

        {

            lock (_lock)

            {

                BuildSiteMap();

                return _root;

            }

        }

 

        public override SiteMapNode FindSiteMapNode(HttpContext context)

        {

            var node = base.FindSiteMapNode(context);

            return node;

        }

 

        public override SiteMapNode FindSiteMapNodeFromKey(string key)

        {

            var node = base.FindSiteMapNodeFromKey(key);

            return node;

        }

 

        public override SiteMapNode FindSiteMapNode(string rawUrl)

        {

            var node = base.FindSiteMapNode(rawUrl);

            return node;

        }

 

        static int contadorIsAccessibleToUser = 0;

        public override bool IsAccessibleToUser(HttpContext context, SiteMapNode node)

        {

            //If Security Trimming is not enabled return true

            if (!SecurityTrimmingEnabled)

                return true;

 

            if (node.Title == "Inicio")

                return true;

 

            //si no es el raiz y no tenemos permisos para el padre no hay permisos para el nodo

            if (node.Title != "Inicio" && node.ParentNode == null)

                return false;

 

            //If there are no roles defined for the page

            //return true or false depending on your authorization scheme (when true pages with

            //no roles are visible to all users, when false no user can access these pages)

            if (node.Roles == null || node.Roles.Count == 0)

                return true;

 

 

            //check each role, if the user is in any of the roles return true

            //si estamos en la carga del arbol de roles

            if (_EsCargaArbol && _rolesIniciales != "")

            {

                //quitamos los reestrictivos

                var rolesNoReestrictivos1 = node.Roles.Cast<string>().Where(role => role.IndexOf("-") != 0).ToList();

                if (rolesNoReestrictivos1.Count == 0)

                    return true;

 

                return rolesNoReestrictivos1.Any(role => _rolesIniciales.Contains(role));

            }

 

            //tenemos que controlar los que son negativos y los que son positivos, los negativos son reestrictivos, por los que los positivos los vamos metiendo en un list

            var rolesNoReestrictivos = new List<string>();

            foreach (string role in node.Roles)

            {

                if (role.IndexOf("-", System.StringComparison.Ordinal) == 0)

                {

                    if (context.User.IsInRole(role))

                        return false;

                }

                else

                    rolesNoReestrictivos.Add(role);

            }

 

            if (rolesNoReestrictivos.Count == 0)

            {

                //si no hay nodos no reestrictivos y hemos llegado aquí quiere decir que si hay algún nodo es restrictivo pero no aplica a este usuario, por

                //lo que ponemos asterisco en todos los roles del menu del sitemap (imprescindible para que la navegación del menu funcione correctamente)

                for (var i = 0; i < node.Roles.Count; i++)

                {

                    node.Roles[i] = "*";

                }

                return true;

            }

 

            //ahora comprobamos si el usuario tiene rol de la pagina

            var res = rolesNoReestrictivos.Any(role => context.User.IsInRole(role) || String.Equals(role, "*", StringComparison.InvariantCultureIgnoreCase));

 

            return res;

 

        }

 

 

        private SiteMapNode GetParentNodeFromDataReader(NodoSiteMap reader)

        {

            // Get the parent ID from the DataReader

            var pid = (int)reader.IdNodoPadre;

            // Return the parent SiteMapNode

            return _nodes[pid];

        }

 

           }

}

 

 

juan ladetto

unread,
Oct 26, 2015, 4:27:56 PM10/26/15
to altnet-...@googlegroups.com
Carlos... podés acceder a la db donde se guarda la info de sesión?
Podés ver si están persistiéndose datos?
Los machinekey seguro que son iguales?

Saludos
Juan

Carlos Admirador

unread,
Oct 27, 2015, 5:47:53 PM10/27/15
to AltNet-Hispano
Sí, los machine.keys son iguales.

Podría pedir acceso a la BD y poder verlo. Qué tablas y campos son ? para poder ver esa info de sesión y cómo la guarda...
...
Reply all
Reply to author
Forward
0 new messages