Session Management in a Broadleaf Commerce Cluster
We are often asked how to configure Broadleaf Commerce in a cluster while allowing for session management across that cluster. For implementations that will have session state (i.e. not a headless API implementation), there are a few approaches that can be used. For this article, I want to explore the recommended configuration and approaches for managing sessions.
When setting up a Broadleaf cluster with session state, the recommended configuration is to use session affinity (e.g. sticky sessions). The primary advantage for session affinity is the ability to maximize cache usage. For performance reasons, Broadleaf utilizes caching strategies - via Hibernate (Level 2 caching) and one-off application-managed caches where needed. With session affinity, a cache is able to be primed/seeded with data for a specific customer. Consecutive requests to the same application server allows for optimal cache efficiency for routine data elements such as customer data, order data, etc.
With a Broadleaf Commerce cluster, an approach needs to be established to allow sessions to be persisted and made available to other nodes. This allows the application to support session failover to another node, should that be needed. Typically, the management of sessions has not been a direct concern for the application, but rather a concern of the server (e.g. Servlet container). With the availability of Spring Session, the configuration of the session management approach has been pushed into the application. While there are architectural decisions to be made - like using Redis vs. JDBC - the setup is managed within Spring.
Out of the box, Spring Session provides for several persistence options for the session data. The current list includes:
- Redis - standalone, cluster, and Sentinel are all supported
- JDBC - storing sessions in the database
- Hazelcast - storing sessions in a Hazelcast-backed repository
There will be differing Maven dependencies and Spring configurations, depending on which persistence layer is used. For this article, I will provide the steps necessary to set up a JDBC datasource as the persistence layer.
This example targets Broadleaf Commerce 5.2+ and should work with Spring Boot or when deployed as a WAR to a Servlet container. Here are the key steps to get it setup.
1. Add Dependency for Spring Session JDBC
This can be added to the pom.xml of the admin and/or the site project depending on which one will use session failover. This example specifically targets SpringSession release 2.0.10-RELEASE which is the latest 2.0.x release as of the writing of this article:
<dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session-jdbc</artifactId> <version>2.0.10.RELEASE</version> </dependency>
2. Session Configuration Class
Create a configuration class that sets up the required Java beans. See the JdbcSessionConfig
class below for an example.
The two bean definitions defined are needed:
- The
propertySourcesPlaceholderConfigurer()
bean is required to prevent the Broadleaf property merging process from causing Spring startup errors. There are property files in Broadleaf that use standard Spring placeholder substitution syntax (e.g.${“propertyName”}
) and on startup Spring will attempt to evaluate the placeholders but is not able to resolve the properties. - The
springSessionConversionService()
bean defines additional type converters - the default type converters may be sufficient but in testing I found additional serialization use cases that required these converters.
It is important to note that any objects you place in your Session will need to be marked with the Serializable interface.
@Configuration @ConditionalOnProperty(value = "spring.session.enabled", havingValue = "true", matchIfMissing = false) @EnableJdbcHttpSession /** * * @author dcolgrove * */ public class JdbcSessionConfig { @Bean public PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() { PropertySourcesPlaceholderConfigurer placeholderConfigurer=new PropertySourcesPlaceholderConfigurer(); placeholderConfigurer.setIgnoreUnresolvablePlaceholders(true); placeholderConfigurer.setIgnoreResourceNotFound(true); return placeholderConfigurer; } @Bean public ConversionService springSessionConversionService() { DefaultConversionService service = new DefaultConversionService(); service.addConverter(Object.class, byte[].class, new SerializingConverter()); service.addConverter(byte[].class, Object.class, new DeserializingConverter()); return service; } }
3. Enable the use of Spring Session and disable the auto configuration
These two settings will need to go into your properties file. I’ve added autoconfiguration
exclusion of Spring Session to avoid other session persistence dependencies (e.g. Redis) from getting referenced.
spring.session.enabled=true spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.session.SessionAutoConfiguration
4. Create the Spring Session tables in your database
The schema for various database platforms is available in the Spring Session repo.
This is all that is needed. With these configurations in place, sessions will be persisted across application restarts or in the event of a session failover if a node in the cluster becomes unavailable.