Sessions in Traditional Load Balancing Environments

In general, we use in-memory sessions (data stored in RAM) in all Web Applications. It works well in most of the traditional hosting environments where we host our application in a dedicated VM or any shared hosting plans.

However, when traffic grows, we plan for load balancing by creating multiple Web Servers and controlling the traffic using Load Balancers. In these scenarios, the session wouldn’t work as the requests (related to a single session) would be served by multiple servers (the same server to serve the requests of a single session is also possible, but it’s not suggested). The solution is to go for storing the sessions in a SQL Server which is accessible across all the Web Servers of the Load Balancing environment.

Sessions in Azure auto-scale environment

Instead of a theoretical discussion on this, let’s directly jump into some practical discussion by developing a small program that uses sessions and see how sessions work in Azure.

Let’s create a Website (you can create MVC Application as well) with the following Web Pages and deploy the application as shown in one of my previous articles,

Login.aspx

This page just accepts the username and password. On “Login” button click, create a session and store the value of the logged-in username in a session and redirect the user to the “Default.aspx”.

Please note that the page also displays the IPAddress “10.202.116.91” of the Virtual Machine where the web site is hosted.

Default.aspx

This page displays the following messages based on the value in the Session.

If Session has some value, then it displays the “Logged in user as admin. You are in 10.202.116.91” as shown in the below screen capture.


If the session doesn’t have any value, then it displays the “Session is NULL. You are in 10.202.116.91” as shown in the below screen capture.

Please notice that the IPAddress of the Server in both the Login.aspx and the Default.aspx is the same. All the requests are being re-directed to the same server.

Also, as per the below screen capture, the sessions are maintained using a Cookie named “ASP.NET_SessionID”. This should be familiar to most of us.

ASP.NET_SessionID

Let’s assume that the traffic to our website has grown and we would like to scale it to two Instances. Let’s go ahead and increase the Instance count to two.

Note

Please note that you cannot increase the instance count in Free and Shared tiers. Your App Service should be in “Basic”, “Standard” or “Premium” tiers.

Service

Let’s again access the Login Page and access it multiple times by refreshing the page. Refreshing the page any number of times will NOT change the IPAddress.

You will notice that the same Web Server is serving the pages. Though we enabled scaling to two instances, the App Service is still serving the requests from one server.

This might be a problem in real-world scenarios because the same server is serving the requests even though Load Balancing is enabled and the other servers are not being utilized properly.

The reason why all the requests are being served is because of the ARR Cookie as shown below. For more information, please have a look here.

Cookie

In Simple terms, this cookie has the Server information from which the initial request has been served so that all subsequent requests from the same session are served by the same VM.

Let’s disable the ARR cookie feature by navigating to the App Service’s Application Settings as shown below.

Settings

Turn off the “ARR Affinity” as shown in the above screen capture and click on “Save” button to save the changes. Please restart the App Service just to clear all the sessions.

Please be a bit cautious from now onwards. The following couple of paragraphs are a bit confusing.

Important

Turning off the ARR Affinity will disable the process of creating the cookie ARRAffinity cookie. So, when the cookie is disabled, the requests might be sent to any of the available servers. So, there is no guarantee that the Sessions will be maintained properly. Sessions will work ONLY when the requests are being served from the Same server. If any of the requests are served by another server (instead of the one where the session is stored) then Sessions will be NULL.

Now, come back to the Web App and navigate to the Login page and notice the new IPAddress “10.202.174.84”. (In your case, initially you might see the same IPAddress as before. Refreshing the page would change the IPAddress. In my case, I refreshed the page twice to get the new IPAddress as shown below.)

IPAddress

Clicking on the Login button will NOT guarantee that the form will be posted to 10.202.174.84. It might post the data to the other server which in my case is “10.202.116.91”.

In my case, when I clicked on the “Login” button of the above screen capture, it took me to the Default page with the following values.

  • IP Address is 10.202.174.84 (this is the same in my login Page).
  • Session is NULL. The reason is, when login is clicked, the request might have gone to the other instance (“10.202.116.91”) where my session got stored.

    Session
    Refresh the page a couple of times.

page
Please observe the following.

  • Session has some value “admin”
  • IPAddress is 10.202.116.91

So, the conclusion here is “Sessions” wouldn’t work as expected in the Azure App Service when you configure Load Balancer using the auto-scaling feature.

Here comes the savior. The Redis Cache provider. Below is the definition from the Azure Official Web site.

Azure Redis Cache is based on the popular open-source Redis cache. It gives you access to a secure, dedicated Redis cache, managed by Microsoft and accessible from any application within Azure

Below are the steps required to make our sessions work as expected.

  • Create the Redis Cache from the Azure Management Portal
  • Configure the Application to use the Azure Redis Cache
  • Use the Sessions

Create the Redis Cache from the Azure Management Portal

Let’s start creating the Redis Cache using the Azure Management Portal as shown below.

Portal

Provide the details of the Redis Cache as shown below.

details

Click on the “Create” button of the above screen capture. It will take a couple of minutes to create the Redis Cache.

Configure the Application to use the Azure Redis Cache

Let’s configure the application to use the Redis Cache that we have just created.

There is a Nuget package called StackExchange.Redis to utilize the Redis Cache. Let’s go ahead and add the package to the application as shown below.

package

As shown in the above screen capture, go-to Package Manager Console and type the command “Install-Package StackExchange.Redis” and click “Enter”,

Console

Now, we have successfully installed the required packages. (Please note that your .NET framework should be 4 or higher.)

In order to use the assemblies, we need to first added the following namespace to both our Login.aspx page and Default.aspx page.

using StackExchange.Redis;

Let’s add a new class named “RedisConnection” to the project. Please refer the attached Project.

In order to connect to Redis Cache, we need to pass the following information to the ConnectionMultiplexer.Connect function. Let’s grab them from the Portal,

  • Redis Cache URL

    Redis Cache URL

  • Keys

    Keys

Please don’t save the Keys in Code. To make things simple, I am storing the keys in the Source Code. Please refer this page which provides information on how to store the credentials.

We are now ready with the class that could be used to connect to the Redis Cache. Let’s use the class to create session in the Redis Cache.

Open your Login.aspx.cs file and replace the following line of code,

  1. Session[“login”] = this.txtUsername.Text.Trim();
With
  1. IDatabase cache= RedisConnection.Connection.GetDatabase();
  2. cache.StringSet(“login”this.txtUsername.Text.Trim());

Open the Default.aspx.cs and replace the following line of code

  1. protected void Page_Load(object sender, EventArgs e)
  2.     {
  3.         if(Session[“login”]==null)
  4.         {
  5.             Response.Write(“Session is NULL.” + ” You are in “ + Request.ServerVariables[“LOCAL_ADDR”]);
  6.         }
  7.         else
  8.         {
  9.             Response.Write(“Logged in user is “ + Session[“login”] + “. You are in “ + Request.ServerVariables[“LOCAL_ADDR”]);
  10.         }
  11.     }

With,

  1. protected void Page_Load(object sender, EventArgs e)
  2. {
  3.     IDatabase cache = RedisConnection.Connection.GetDatabase();
  4.     string strLoginValue = cache.StringGet(“login”);
  5.     if (strLoginValue == null)
  6.     {
  7.         Response.Write(“Session is NULL.” + ” You are in “ + Request.ServerVariables[“LOCAL_ADDR”]);
  8.     } else
  9.     {
  10.         Response.Write(“Logged in user is “ + strLoginValue + “. You are in “ + Request.ServerVariables[“LOCAL_ADDR”]);
  11.     }
  12. }

Let’s deploy the code to the Azure App Service and review the changes and navigate to the Login Page as shown below.


Notice the IPAddress. It’s 10.202.174.84. Now click on “Login” button. You will be taken to the Default.aspx page as shown below.

Notice that in the above screen capture, though the IPAddress is different, still we are able to see the username “admin” because the sessions are now stored in a distributed location “Redis Cache” which is accessible for both the instances.

Now, keep refreshing the page multiple times. You will notice that the IP Addresses are changing but not the value of the username.

That’s it. We have learned how to store the sessions in a load Balancing Environment. You can store any kind of data in Redis Cache.

Hope you enjoyed reading the article. Your feedback is really appreciated.