Logo

Maarten Balliauw {blog}

ASP.NET, ASP.NET MVC, Azure, PHP, OpenXML, VSTS, ...

About the author

Maarten Balliauw is an MVP ASP.NET and is currently employed as .NET Software Engineer at RealDolmen. His interests are mainly web applications developed in ASP.NET (C#) or PHP.
More about me More about me
Send mail E-mail me


Microsoft Most Valuable Professional - MVP - ASP.NET

Subscribe to my RSS feed Follow me on Twitter! View Maarten Balliauw's profile on LinkedIn RealDolmen - Rock-solid passion for ICT
I'm a speaker at TechDays Belgium and TechDays Finland

Search

Latest Twitter

    Follow me on Twitter...

    Disclaimer

    The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

    © Copyright Maarten Balliauw 2010

    ASP.NET Session State Partitioning using State Server Load Balancing

    It seems like amount of posts on ASP.NET's Session State keeps growing. Here's the list:

    Yesterday's post on Session State Partitioning used a round-robin method for partitioning session state over different state server machines. The solution I presented actually works, but can still lead to performance bottlenecks.

    Let's say you have a web farm running multiple applications, all using the same pool of state server machines. When having multiple sessions in each application, the situation where one state server handles much more sessions than another state server could occur. For that reason, ASP.NET supports real load balancing of all session state servers.

    Download example

    Want an instant example? Download it here.
    Want to know what's behind all this? Please, continue reading.

    What we want to achieve...

    Here's a scenario: We have different applications running on a web farm. These applications all share the same pool of session state servers. Whenever a session is started, we want to store it on the least-busy state server.

    1. Performance counters

    To fetch information on the current amount of sessions a state server is storing, we'll use the performance counters ASP.NET state server provides. Here's a code snippet:

    if (PerformanceCounterCategory.CounterExists("State Server Sessions Active", "ASP.NET", "STATESERVER1")) {
        PerformanceCounter pc = new PerformanceCounter("ASP.NET", "State Server Sessions Active", "", "STATESERVER1");
        float currentLoad = pc.NextValue();
    }

    2. Creating a custom session id

    Somehow, ASP.NET will have to know on which server a specific session is stored. To do this, let's say we make the first character of the session id the state server id from the following IList:

    IList<StateServer> stateServers = new List<StateServer>();

    // Id 0, example session id would be 0ywbtzez3eqxut45ukyzq3qp
    stateServers.Add(new StateServer("tcpip=10.0.0.1:42424", "sessionserver1"));

    // Id 1, example session id would be 1ywbtzez3eqxut45ukyzq3qp
    stateServers.Add(new StateServer("tcpip=10.0.0.2:42424", "sessionserver2"));

    Next thing we'll have to do is storing these list id's in the session id. For that, we will implement a custom System.Web.SessionState.SessionIDManager class. This class simply creates a regular session id, locates the least-busy state server instance and assign the session to that machine:

    using System;
    using System.Diagnostics;


    public class SessionIdManager : System.Web.SessionState.SessionIDManager
    {
        public override string CreateSessionID(System.Web.HttpContext context)
        {
            // Generate a "regular" session id
            string sessionId = base.CreateSessionID(context);

            // Find the least busy state server
            StateServer leastBusyServer = null;
            float leastBusyValue = 0;
            foreach (StateServer stateServer in StateServers.List)
            {
                // Fetch first state server
                if (leastBusyServer == null) leastBusyServer = stateServer;

                // Fetch server's performance counter
                if (PerformanceCounterCategory.CounterExists("State Server Sessions Active", "ASP.NET", stateServer.ServerName))
                {
                    PerformanceCounter pc = new PerformanceCounter("ASP.NET", "State Server Sessions Active", "", stateServer.ServerName);
                    if (pc.NextValue() < leastBusyValue || leastBusyValue == 0)
                    {
                        leastBusyServer = stateServer;
                        leastBusyValue = pc.NextValue();
                    }
                }
            }

            // Modify session id to contain the server's id
            // We will change the first character in the string to be the server's id in the
            // state server list. Notice that this is only for demonstration purposes! (not secure!)
            sessionId = StateServers.List.IndexOf(leastBusyServer).ToString() + sessionId.Substring(1);

            // Return
            return sessionId;
        }
    }

    The class we created will have to be registered in web.config. Here's how:

    <configuration>
      <system.web>
        <!-- ... -->
        <sessionState mode="StateServer"
                    partitionResolverType="PartitionResolver"
                    sessionIDManagerType="SessionIdManager" />
        <!-- ... -->
      </system.web>
    </configuration>

    You notice our custom SessionIdManager class is now registered to be the sessionIDManager. The PartitionResolver I blogged about is also present in a modified version.

    3. Using the correct state server for a specific session id

    In the previous code listing, we assigned a session to a specific server. Now for ASP.NET to read session state from the correct server, we still have to use the PartitionResolver class:

    using System;


    public class PartitionResolver : System.Web.IPartitionResolver
    {

        #region IPartitionResolver Members

        public void Initialize()
        {
            // No need for this!
        }

        public string ResolvePartition(object key)
        {
            // Accept incoming session identifier
            // which looks similar like "2ywbtzez3eqxut45ukyzq3qp"
            string sessionId = key as string;

            // Since we defined the first character in sessionId to contain the
            // state server's list id, strip it off!
            int stateServerId = int.Parse(sessionId.Substring(0, 1));

            // Return the server's connection string
            return StateServers.List[stateServerId].ConnectionString;
        }

        #endregion

    }

    kick it on DotNetKicks.com


    Categories: ASP.NET | C# | General | ICT | Internet | Software | Webfarm | XML

    Comments

    Fenil Desai India | Reply

    Friday, January 25, 2008 6:36 AM

    Fenil Desai

    There can't be a better post on Session Partitioning that this one.
    Absolutely Amazing......

    Scott Hanselman United States | Reply

    Thursday, January 31, 2008 8:18 PM

    Scott Hanselman

    Fantastic series! One question, however, isn't there measurable overhead in the creation of that remote Performance Counter? How much optimization have you had to do in that CreateSessionID call?

    hanselman.com | Reply

    Thursday, January 31, 2008 8:29 PM

    pingback

    Pingback from hanselman.com

    Scott Hanselman's Computer Zen - Troubleshooting Expired ASP.NET Session State and Your Options

    maartenba Belgium | Reply

    Thursday, January 31, 2008 9:27 PM

    maartenba

    Thank's Smile The remote performance counter will definitely give some overhead, but I did not measure this in a real-life environment.

    lianglisen People's Republic of China | Reply

    Wednesday, December 30, 2009 8:37 PM

    lianglisen

    About your article “ASP.NET Session State Partitioning using State Server Load Balancing”
    I want to ask something:I have download and  test you code from maarten balliauw blog,is the webserver appication store the session on the state server one by one?e.g:1st connection,session store in state server A, 2nd connection,session store in state server B,....... cycle again and again。Hoping to get you help.Best Regards.

    maartenba Belgium | Reply

    Wednesday, December 30, 2009 9:48 PM

    maartenba

    Yes, that's correct. Unless the performance counter tells there's too much load, the server is skipped for storing state. Note that this is just an example and should not be used in production without further fine tuning.

    weblogs.asp.net | Reply

    Friday, February 01, 2008 1:55 AM

    pingback

    Pingback from weblogs.asp.net

    Maarten Balliauw on ASP.NET load balancing and the ASP.NET state server. - Joe On ASP.NET

    blogs.msdn.com | Reply

    Friday, February 01, 2008 1:55 AM

    pingback

    Pingback from blogs.msdn.com

    Joe Stagner - Frustrated by Design ! : Maarten Balliauw on ASP.NET load balancing and the ASP.NET state server.

    geeks.ms | Reply

    Friday, February 01, 2008 2:48 AM

    pingback

    Pingback from geeks.ms

    Maarten Balliauw on ASP.NET load balancing and the ASP.NET state server. - Noticias externas

    radicaldevelopment.net | Reply

    Sunday, February 03, 2008 9:24 AM

    pingback

    Pingback from radicaldevelopment.net

    January 2008 Resources RoundUp - Radical Development

    Yolion People's Republic of China | Reply

    Wednesday, February 20, 2008 6:19 AM

    Yolion

    Great

    Elan United States | Reply

    Tuesday, March 18, 2008 5:05 PM

    Elan

    How does this address failover? Am I correct in assuming that when a server goes down, the if (PerformanceCounterCategory.CounterExists) doesn't blow the whole thing up and also prevents that [offline] server from being assigned to store this session state?

    But then what happens if a user's session state had been assigned to the server that just went down? Does the PartitionResolver throw an error? Perhaps to make this more robust, the exception handling can be set to reset and reassign the session? Then the worst that happens to the client is a lost session - but no error messages.

    maartenba Belgium | Reply

    Tuesday, March 18, 2008 5:08 PM

    maartenba

    This example code does NOT include any checks on remote server existance. You would have to check if the state server is running in the code prior to assigning a session to it without failure massages.

    Elan United States | Reply

    Tuesday, March 18, 2008 6:58 PM

    Elan

    if (PerformanceCounterCategory.CounterExists("State Server Sessions Active", "ASP.NET", stateServer.ServerName))

    Isn't that an inherent check on whether the State Server is operating? Or will you error out entirely if the machinename reference leads to a server that is down?

    If it works properly as is, then couldn't you include this check in the PartitionResolver class and handle appropriately if your session is pointing at a server that can no longer be found?

    maartenba Belgium | Reply

    Tuesday, March 18, 2008 7:58 PM

    maartenba

    As far as I can see there are 2 things that can go wrong:
    1) The performance counter is unavailable
    2) State server is unavailable

    Possible solutions:
    1) Check for Exceptions on PerformanceCounterCategory.CounterExists(...), respond to Exceptions by trying the next server. If it fails > X times, remove it from the list of servers.
    2) Respond to possible Exceptions. If it fails > X times, remove it from the list of servers.

    blogs.msdn.com | Reply

    Tuesday, July 08, 2008 9:01 AM

    pingback

    Pingback from blogs.msdn.com

    JoeOn.net In Japanese : Windows Workflow Foundation チュートリアル シリーズ

    sss United States | Reply

    Wednesday, July 16, 2008 12:55 PM

    sss

    Pingback from blogs.msdn.com

    JoeOn.net In Japanese : Windows Workflow Foundation チュートリアル シリーズ

    Chris United Kingdom | Reply

    Tuesday, September 02, 2008 3:44 PM

    Chris

    This sounds absolutely fab!! I have tried implementing it but have failed. How do I implement this in IIS? thanks

    Echte Rotterdamer United Kingdom | Reply

    Saturday, November 01, 2008 8:50 AM

    Echte Rotterdamer

    Lets try it again. Your code is flawed in 2 places:

    1.SessionIdManager - if you have two state servers in the list one will never get hit.

    2.ParftitionResolver - When a state server goes down all live sessions for that server will raise an exception. If that exception is handled in Application_Error handler in Global.asax by redirecting to a custom error page (common scenario) and that page try to access session it will hit PartitionResolver again creating a nasty loop. If you have enough live sessions for the dead state server it might bring your application down.

    To overcome the problem you have to catch that exception, create a new sessionID, recreate session object for the current context with that new sessionID. Sure user will loose the session but the application will recover gracefully and they can start a new session.

    SamR United States | Reply

    Tuesday, November 18, 2008 9:30 PM

    SamR

    Hey All,

    We have a website that is running on 2 boxes, and every other click we lose our sessions in production. This doesn't happen in local machine at all. Should I try switching to SQL Server or partitioning.
    Thank u.

    maartenba Belgium | Reply

    Wednesday, November 19, 2008 7:48 AM

    maartenba

    Sam, I guess you should consider both options: SQL server will be more reliable but a little slower, state server / partitioning will be a bit faster but there's still possible loss of session data.

    Echte Rotterdamer United Kingdom | Reply

    Monday, November 24, 2008 4:45 PM

    Echte Rotterdamer

    Sam

    Maarten's post was the great starting point and I am really greatful to him for helping me kick-start my own project. I have done some further work and now I have the solution we are going to use in our production environment. You are free to use it if you wish, you can read about if you visit the following link:

    en.aspnet-bhs.info/.../...Server-Partitioning.aspx

    RealDolmen blogs | Reply

    Wednesday, December 31, 2008 3:43 PM

    trackback

    Trackback from RealDolmen blogs

    Top blog posts in 2008

    answerspluto.com | Reply

    Monday, July 13, 2009 9:38 PM

    pingback

    Pingback from answerspluto.com

    list of urls 4 « Answers Pluto

    Microsoft RealDolmen blogs | Reply

    Monday, January 04, 2010 8:55 AM

    trackback

    Byebye 2009, welcome 2010!

    Byebye 2009, welcome 2010!

    Add comment




      Country flag

    biuquote
    • Comment
    • Preview
    Loading