Professor Sloth

Free web performance master class

Learn about web performance and how to make your site faster, delivered straight to your inbox.

Episode 16: Using Redis for Distributed User Sessions in ASP.NET Core

We need distributed session state because load balancing with sticky sessions is whack. Spoiler: We DON’T roll it ourselves. ASP.NET session storage is useful for storing state across page views. In single server situations it’s simple to set up because ASP.NET supports in-memory session out of the box. In-memory sessions stop working as soon as there is more than one server. Most production environments have more than one server so the session issue needs to be dealt with.

There are two options for sessions in a web farm. First, a load balancer can be used to lock each user on a specific box (so-called “sticky sessions” or “session affinity”). This lets us continue to use in-memory session. The second is switching from in-memory to distributed session storage.

We will use a distributed session using Redis because we have existing Redis infrastructure and we don’t want a complex load balancer added to the mix.

Install the StackExchange Redis Distributed Cache Package

We already use StackExchange.Redis for talking to Redis. Conveniently, Microsoft has a ready-made distributed cache package which uses StackExchange.Redis. The first step is to install it:

PM> Install-Package Microsoft.Extensions.Caching.StackExchangeRedis

Configure Redis Based Distributed Session in ASP.NET Core

The application must be configured to use the StackExchangeRedis cache. Be sure to tell the cache which Redis instance to connect to:


using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using StackExchange.Redis;

namespace MyApp
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            // Configure Redis Based Distributed Session
            var redisConfigurationOptions = ConfigurationOptions.Parse("localhost:6379");

            services.AddStackExchangeRedisCache(redisCacheConfig =>
            {
                redisCacheConfig.ConfigurationOptions = redisConfigurationOptions;
            });

            services.AddSession(options => {
                options.Cookie.Name = "myapp_session";
                options.IdleTimeout = TimeSpan.FromMinutes(60 * 24);
            });

            // ...snip... Other ASP.NET Core configuration here!
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            // Enable session for the application
            app.UseSession();

            // ...snip... Other ASP.NET Core configuration here!
        }
    }
}
Startup.cs

Read and Write to the User’s Session

With the Redis cache configured, reading and writing to the user’s session should work:


using System;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;

namespace MyApp.Controllers
{
    public class TestController : Controller
    {
        [HttpGet]
        [Route("/write-to-session")]
        public IActionResult TestWriteSession()
        {
            var value = $"Session written at {DateTime.UtcNow.ToString()}";
            HttpContext.Session.SetString("Test", value);

            return Content($"Wrote: {value}");
        }

        [HttpGet]
        [Route("/read-from-session")]
        public IActionResult TestReadSession()
        {
            var value = HttpContext.Session.GetString("Test");

            return Content($"Read: {value}");
        }
    }
}
TestController.cs

Now distributed session and user authentication are both working. Next, we’ll create a sign-up flow for users to create their accounts in the first place.

Jordan Griffin
VP Engineering Request Metrics