Intro
By default, Sitecore uses an in-proc session state provider which is sufficient for the local/development environment but not so much when you have multiple Content Delivery (CD) servers and you need to offload the session store to another server and have Sitecore manage it all for you. As always, I am not going to talk about performance among different session state providers (e.g. Redis, SQL Database, etc.) as those discussions have already been covered countless times before and I feel that they are self-explanatory at this point.
Also, I am assuming that you're already familiar with Redis and you have the Redis server setup and ready to go. With that said, I will try to cover how you can have Sitecore use an out-of-process session state store. And I will also try to cover some of the gotchas that I've come across that you need to be aware of.
Update - 11/06/2018
Sitecore also provides their own custom implementation of Session Provider (called Sitecore.SessionProvider.Redis) that I'd highly recommend using instead of the package I am referring below. A quick glance at the decompiled assembly tells me that they are doing a lot more than what Microsoft Session State Provider does that is unique to Sitecore (e.g. handling Session_End event). To be clear, I chose to use Microsoft's implementation because it exposes ISerialize interface that allows more control over serialization as I am storing custom data into the session for a user.
Implementation
Let's go through and cover the entire implementation.
Install NuGet Packages:
Install-Package Microsoft.Web.RedisSessionStateProvider
Install-Package Microsoft.AspNet.SessionState.SessionStateModule
Create Custom Serializer
Let's create a custom session store serializer that will facilitate the process of successfully serializing and de-serializing Sitecore data by storing it as a binary stream into Redis.
/// <summary>
/// A custom session state serializer that is used by the site to handle serialization/de-serialization process.
/// Please note that this handles BinarySerializer (default behavior which is suitable for Sitecore).
/// </summary>
public class RedisSessionStoreCustomSerializer : ISerializer
{
private static readonly BinarySerializer BinarySerializer = new BinarySerializer();
public object Deserialize(byte[] data)
{
return data == null ? null : BinarySerializer.Deserialize(data);
}
public byte[] Serialize(object data)
{
return BinarySerializer.Serialize(data ?? string.Empty);
}
}
Tip:
If you're using JsonSerializer for other things in your project, you can modify the above class to use that based on whether or not it's a Sitecore content. But keep in mind that currently, Sitecore does not play well when using JsonSerializer so it's better to use BinarySerializer for Sitecore related data and JsonSerializer for the site related data.
Now, let's modify the connection string as well as web config transform(s) for the environments that are load balanced (e.g. staging/production) as seen below. Of course, you'll need to provide appropriate settings based on your own setup but you can use the configuration provided below to start:
Connection String Changes:
<!--This key represents where the Redis server endpoint is. -->
<add name="sharedSession" connectionString="localhost:6379" />
Web Config Changes:
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<appSettings>
<!--This setting is important as it allows concurrent requests coming from different servers to access the same session. -->
<add key="aspnet:AllowConcurrentRequestsPerSession" value="true" xdt:Transform="InsertIfMissing" xdt:Locator="Match(key)"/>
</appSettings>
<system.web>
<sessionState mode="InProc" xdt:Transform="Remove" xdt:Locator="Match(mode)" />
<sessionState customProvider="CSFRedisSessionStoreCache" mode="Custom" cookieless="false" timeout="30" xdt:Transform="InsertIfMissing">
<providers>
<add
name="CustomRedisSessionStoreCache"
type="Microsoft.Web.Redis.RedisSessionStateProvider"
redisSerializerType = "RedisSessionStoreCustomSerializer, TestApp"
connectionString="sharedSession"
databaseId = "0"
applicationName = "TestApp"/>
</providers>
</sessionState>
</system.web>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true" xdt:Transform="Replace">
<!--Let's remove current Session tag and replace it with our Async module.-->
<remove name="Session" />
<add name="Session" type="Microsoft.AspNet.SessionState.SessionStateModuleAsync, Microsoft.AspNet.SessionState.SessionStateModule, Version=1.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" preCondition="integratedMode" />
</modules>
</system.webServer>
</configuration>
Now, let's do the build and see whether we see Redis being used as a session store by Sitecore. I prefer to use Redis Desktop Manager application when looking at the data stored in Redis but you can use any other tool readily available or connect directly to Redis server and execute commands to see what's stored on the server.
See the screenshot below for the sessions data being stored in Redis after going thru this configuration:
Marketing Analytics Service
For those of you who are on Sitecore 9, make sure to change the tracking analytics service to use Out-of-process session store as well. If you don't do this then your site will not work as expected. Here's the patch transform that I am using to overwrite the default settings for the service.
<tracking>
<sharedSessionState defaultProvider="InProc">
<patch:attribute name="defaultProvider">redis</patch:attribute>
<providers>
<clear />
<add name="InProc" type="System.Web.SessionState.InProcSessionStateStore">
<patch:attribute name="name">redis</patch:attribute>
<patch:attribute name="type">Sitecore.SessionProvider.Redis.RedisSessionStateProvider, Sitecore.SessionProvider.Redis</patch:attribute>
<patch:attribute name="connectionString">sharedSession</patch:attribute>
<patch:attribute name="pollingInterval">2</patch:attribute>
<patch:attribute name="applicationName">shared</patch:attribute>
</add>
</providers>
</sharedSessionState>
</tracking>
Tip:
The applicationName attribute above needs to be either set to private or shared. For the load-balanced environment, this should be set to shared.
Additional Resources/links
Listed below are some of the official Sitecore resources I found to be quite useful: