Try our conversational search powered by Generative AI!

Marco ter Horst
Oct 10, 2011
  13478
(4 votes)

Load balancing EPiServer CMS

Load balancing EPiServer can be quite tricky and there are a few key configuration items to be set.
All the information below has been gathered from several different articles, blog posts, etc. on the Internet. The purpose of this blog is to provide you with the tools to successfully setup a Load balanced EPiServer environment.

Introduction

For one of our customers we have the following EPiServer setup:
Functional:
A customer website with both a public and a private pages, using Forms bases authentication + (of course) EPiServer CMS pages.
The public part of website is available over http, the private and CMS pages over https.
Customers seamlessly roam back and forth between http and https pages.
Technical:
The architecture consists of 3 servers, all virtuals in the TerreMark eCloud: 2 EPiServer application servers and 1 database server.

Web servers:
Windows 2008 R2
4 VPU’s
6GB Memory

Both http and https traffic is load balanced over both application servers. Load balancing is configured with stickiness based on source IP address with a TTL of 5 minutes.
On both application servers the EPiServer application is served by a “Web Garden” of three Worker Processes.
So in effect EPiServer is served by 6 Worker Processes in total.

Database server:

Windows 2008
2 VPU’s
8GB Memory


Next to the standard EPiServer database, the application uses five more databases (i.e. store and product databases).

Arch

 

Challenges

With the setup described above, we are faced with a couple of challenges.

- Content & Cache Updates
- Shared VPP Data
- Unauthenticated Session States
- Authenticated Session States

The first two are actually easy because they are well documented in several EPiServer articles.
For the latter two some .net trickery is required.

Content & Cache Updates
Make sure the following section is uncommented in your web.config on all nodes:

<system.serviceModel>
  <extensions>
    <bindingElementExtensions>
      <add name="udpTransport" type="Microsoft.ServiceModel.Samples.UdpTransportElement, EPiServer.Implementation" />
    </bindingElementExtensions>
  </extensions>
  <services>
    <!-- Before deployment, you should remove the returnFaults behavior configuration to avoid disclosing information in exception messages -->
    <service name="EPiServer.Events.Remote.EventReplication" behaviorConfiguration="DebugServiceBehaviour">
      <endpoint name="RemoteEventServiceEndPoint" contract="EPiServer.Events.ServiceModel.IEventReplication" binding="customBinding" bindingConfiguration="RemoteEventsBinding" address="soap.udp://239.255.255.19:5000/RemoteEventService" />
    </service>
  </services>
  <client>
    <endpoint name="RemoteEventServiceClientEndPoint" address="soap.udp://239.255.255.19:5001/RemoteEventService" binding="customBinding" bindingConfiguration="RemoteEventsBinding" contract="EPiServer.Events.ServiceModel.IEventReplication" />
    <!--Client configuration for the ImageEditor, the client name has to be "ImageServiceClientEndPoint"-->
    <!-- Only uncomment if the Image Service is hosted in an external application
    <endpoint
     name="ImageServiceClientEndPoint"
     address="the address of the hosting application"
     binding="the binding the hosting application is using"
     bindingConfiguration="ImageServiceBinding"
     contract="EPiServer.ImageLibrary.IImageService" />
     -->
  </client>
  <behaviors>
    <serviceBehaviors>
      <behavior name="DebugServiceBehaviour">
        <serviceDebug includeExceptionDetailInFaults="false" />
      </behavior>
    </serviceBehaviors>
  </behaviors>
  <bindings>
    <customBinding>
      <binding name="RemoteEventsBinding">
        <binaryMessageEncoding />
        <udpTransport multicast="True" />
      </binding>
    </customBinding>
    <!-- Only uncomment if the Image Service is hosted in an external application
    <binding type used by the Image Service>
      <binding name="ImageServiceBinding" maxReceivedMessageSize="20000000">
        <readerQuotas maxArrayLength="20000000" />
        <security mode="None" />
      </binding>
    </binding type used by the Image Service>
     -->
  </bindings>
</system.serviceModel>

 

This enables content updates, which are made e.g. on node CMS01 to be propagated to node CMS02.

More information here.

 

Shared VPP Data

Use the following steps to enable use of shared VPP Folder:

1) Create a new user
2) Make this user member of the IIS IUSRS\ group (remove all other group memberships for extra security)
3) Make sure the IIS IUSRS\ group has appropriate rights on “non default” folders. (E.g. Logging folders)
4) Configure your Application Pool to run as this user
5) On the Server with the VPP folders, repeat steps 1) & 2)
6) Give the IIS IUSRS\ group appropriate rights on the VPP Folders (and sub folders)

More information here.

 

Unauthenticated Session States

By default Microsoft Internet Information Server saves session states “In Process”. Meaning in the Worker Process of the web application.
In our scenario we will want to use a Session State Database. Using the ASP.NET State Service (part of default install) to save session states to a database.
Don’t worry about performance. This actually works very well and very fast.
Here is all the info you need to configure your website to use it.

After following the steps from the KB article to setup the DB. You add this piece of configuration to your web.config on all nodes:

<sessionState mode="SQLServer" sqlConnectionString="data source=10.1.1.1;user id=aspstate;password=******" cookieless="false" timeout="20" />

Make sure the user id is created on the DB server and has DBO rights (only) on the ASPstate DB.

 

Authenticated Session States

Picture this scenario:
- a user opens a public page and is routed by the load balancer over port 80 to CMS01
- he clicks a link to use a feature on the website only available for logged in users and is redirected to the login page. This is a SSL encrypted page and the users is routed by the load balancer over port 443 to CMS02
- the user fills in his credentials and is redirected back to the original public page to CMS01

Unless we perform the steps below, the user will not be logged in and the following error will appear in the Windows System Eventlog:

Event code: 4005
Event message: Forms authentication failed for the request. Reason: The ticket supplied was invalid.
Event time: 10/10/2011 2:29:32 PM
Event time (UTC): 10/10/2011 12:29:32 PM
Event ID: 2ddd863e47534f53a182f84a9a613070
Event sequence: 75
Event occurrence: 1
Event detail code: 50201
 
Application information:
    Application domain: /LM/W3SVC/2/ROOT-1-129627223033590000
    Trust level: Full
    Application Virtual Path: /
    Application Path: D:\Inetpub\wwwroot\
    Machine name: CMS01
 
Process information:
    Process ID: 2716
    Process name: w3wp.exe
    Account name: username
 
Request information:
    Request URL:
http://website/UI/javascript/system.aspx
    Request path: /UI/javascript/system.aspx
    User host address: ip address
    User: 
    Is authenticated: False
    Authentication Type: 
    Thread account name: username

 

This is what happened: after the post of login credentials on CMS02. The server encrypted the VIEWSTATE using the node’s machine key.
Now when the users arrived back on the node CMS01, this node was unable to decrypt the VIEWSTATE and therefore did not accept the user as validated user.

To fix this, we need to make sure all servers in the farm use the same machine key.

There are a ton of websites who provide online machine key generation. Like this one: http://aspnetresources.com/tools/machineKey

Just click generate and add it to your web.config. Make sure you add it between system.web tags:

<system.web>
<machineKey validationKey="7F252ED2FAF2BB70FDFB7AF2CC5864981D4933F795FDBC5E5B54DB2589148AF1BD10E277044C0426C6C47601AE2074F3B5E7012D6" decryptionKey="00C0C09D8E7D0DCB3535A29A00E3FBA39DF3300" validation="SHA1"/>
</system.web>

 

Good luck!

Smile

Oct 10, 2011

Comments

Oct 10, 2011 04:50 PM

Nice post! Just one thing, why do you use Web Gardens?

Oct 11, 2011 09:53 AM

I agree with evest, very nice article!

Marco ter Horst
Marco ter Horst Oct 12, 2011 07:35 PM

We see a significant performance boost when we implement a Web Garden.
IIS Request queue drops, CPU usage drops the web application just runs a lot smoother.

There is only one pitfall: the worker processes sometimes grow to 1.2GB memory usage or even more. Therefore we decided to configure 3 (and not more) WP instances and set the max. memory usage for the WP's @ 900MB. So if a WP goes over the 900MB threshold, it's recycled to a fresh WP.

Please login to comment.
Latest blogs
Optimizely and the never-ending story of the missing globe!

I've worked with Optimizely CMS for 14 years, and there are two things I'm obsessed with: Link validation and the globe that keeps disappearing on...

Tomas Hensrud Gulla | Apr 18, 2024 | Syndicated blog

Visitor Groups Usage Report For Optimizely CMS 12

This add-on offers detailed information on how visitor groups are used and how effective they are within Optimizely CMS. Editors can monitor and...

Adnan Zameer | Apr 18, 2024 | Syndicated blog

Azure AI Language – Abstractive Summarisation in Optimizely CMS

In this article, I show how the abstraction summarisation feature provided by the Azure AI Language platform, can be used within Optimizely CMS to...

Anil Patel | Apr 18, 2024 | Syndicated blog

Fix your Search & Navigation (Find) indexing job, please

Once upon a time, a colleague asked me to look into a customer database with weird spikes in database log usage. (You might start to wonder why I a...

Quan Mai | Apr 17, 2024 | Syndicated blog