This tutorial discusses in detail how to configure HTTP Session Management in clustered Web applications, based on the new changes introduced in WildFly 17.
Web applications which are running in a cluster-aware configuration (“ha” or “full-ha” profile) can survive the lifespan of a single server by adding the element into the web.xml file. Example:
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <distributable/> </web-app>
This is sufficient to achieve that your HTTP Session survives a server crash/restart, provided that at least one server is still available. You can further specialize the way HTTP Session is managed in the cluster using different strategies, depending on your WildFly version.
HTTP Session Management in WildFly 17 or newer
WildFly 17 introduced the distributable-web subsystem, which manages a set of session management profiles that encapsulate the configuration of a distributable session manager:
<subsystem xmlns="urn:jboss:domain:distributable-web:2.0" default-session-management="default" default-single-sign-on-management="default">
You can check the default-session-management attribute from the CLI as follows:
/subsystem=distributable-web:read-attribute(name=default-session-management) { "outcome" => "success", "result" => "default" }
The HTTP Session Management is handled, under the hoods,by Infinispan, therefore if you want to check its settings, you have to reach the infinispan-session-management attribute under the distributable-web subsystem. Example, for the “default” infinispan-session-management:
/subsystem=distributable-web/infinispan-session-management=default:read-resource { "outcome" => "success", "result" => { "cache" => undefined, "cache-container" => "web", "granularity" => "SESSION", "affinity" => {"primary-owner" => undefined} } }
As you can see, there are several configurable attribute for the infinispan-session-management:
- cache-container: This references the Infinispan cache-container into which session data will be stored.
- cache: This references a cache for the related cache-container. If undefined, the default cache of the associated cache container will be used.
- granularity: This defines the session manager mapping for the individual cache entries:
- affinity: This attribute defines the affinity that a web request should have for a given server.
Configuring HTTP Session Granularity
The granularity attribute can have the following values:
- SESSION: Stores all session attributes within a single cache entry. This is generally more expensive than ATTRIBUTE granularity, but preserves any cross-attribute object references.
- ATTRIBUTE: Stores each session attribute within a separate cache entry. This is generally more efficient than SESSION granularity, but does not preserve any cross-attribute object references.
If your application does not share any object references between attributes, users are strongly advised to use ATTRIBUTE granularity. Using ATTRIBUTE granularity, each session attribute is stored in a separate cache entry. This means that a given request is only required to replicate/persist those attributes that were added/modified/removed/mutated in a given request. For read-heavy applications, this can dramatically reduce the replication/persistence payload per request.
Here is how you can set the granularity to “ATTRIBUTE” for the default session manager:
/subsystem=distributable-web/infinispan-session-management=default/:write-attribute(name=granularity,value=ATTRIBUTE)
Configuring HTTP Session Affinity
The affinity attribute defines the affinity that an HTTP request has for a given WildFly server. The affinity of the associated web session determines the algorithm for generating the route to be appended onto the session ID (within the JSESSIONID cookie, or when encoding URLs). Possible values are:
- affinity=none: HTTP requests won’t have affinity to any particular node.
- affinity=local: HTTP requests will have an affinity to the server that last handled a request for a given session. This is the standard sticky session behavior.
- affinity=primary-owner: HTTP requests will have an affinity to the primary owner of a given session. This is the default setting. Behaves the same as affinity=local if the backing cache is not distributed nor replicated.
Adding a new Session Management Profile
You can define a new Session Management Profile as follows:
/subsystem=distributable-web/infinispan-session-management=custom-profile/:add(cache-container=web,granularity=ATTRIBUTE)
Now, in order to link the “custom-manager” Session Management Profile to your application, several options are available:
Include a WEB-INF/distributable.xml file in your application, which links your customer profile:
<?xml version="1.0" encoding="UTF-8"?> <distributable-web xmlns="urn:jboss:distributable-web:1.0"> <session-management name="custom-profile"/> </distributable-web>
You can also link the Session Management Profile through the existing jboss-all.xml:
<?xml version="1.0" encoding="UTF-8"?> <jboss xmlns="urn:jboss:1.0"> <distributable-web xmlns="urn:jboss:distributable-web:1.0"> <session-management name="custom-profile"/> </distributable-web> </jboss>
Defining Session Management Profile at application level
It is also possible to use deployment-specific settings for your session management profile. This can be done by adding an infinispan-session-management element into the /WEB-INF/distributable-web.xml. Example:
<?xml version="1.0" encoding="UTF-8"?> <distributable-web xmlns="urn:jboss:distributable-web:1.0"> <infinispan-session-management cache-container="web" cache="demo-cache" granularity="ATTRIBUTE"> <local-affinity/> </infinispan-session-management> </distributable-web>
As an alternative, also the file META-INF/jboss-all.xml can contain deployment specific settings for your session profile:
<?xml version="1.0" encoding="UTF-8"?> <jboss xmlns="urn:jboss:1.0"> <distributable-web xmlns="urn:jboss:distributable-web:2.0"> <infinispan-session-management cache-container="web" granularity="SESSION"> <primary-owner-affinity/> </infinispan-session-management> </distributable-web> </jboss>
Using jboss-web.xml to manage max-active-sessions
Prior to WildFly 17, the file jboss-web.xml contained the core settings for configuring HTTP Session replication. Here is the basic structure of it:
<jboss-web> ... <max-active-sessions>...</max-active-sessions> ... <replication-config> <replication-granularity>...</replication-granularity> <cache-name>...</cache-name> </replication-config> ... </jboss-web>
Since WildFly 17, the replication-config section has been deprecated. You can still use max-active-sessions to impose a limit to the number of currently active sessions. Please notice that this limit works differently, depending if you are running an application in a cluster or not (that is if you are using in web.xml).
- Clustered Web applications: if you reach the max-active-sessions and a new request arrives, the request is accepted and an old session is passivated to disk using a Least Recently Used (LRU) algorithm
- Not Clustered Web applications: when exceeding the max-active-sessions limit, a new request will fail with an IllegalStateException.
Configuring HTTP Session replication before WildFly 17
Prior to WildFly 17, you can rely on jboss.web.xml to configure the replication configuration for HTTP Sessions. For example, the following jboss-web.xml snippet shows how to set the replication-granularity to SESSION for your Web application:
<jboss-web xmlns="http://www.jboss.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.jboss.com/xml/ns/javaee http://www.jboss.org/j2ee/schema/jboss-web_10_0.xsd"> <replication-config> <replication-granularity>SESSION</replication-granularity> </replication-config> </jboss-web>
Older WildFly versions could also configure the replication-trigger of your HTTP Session as in the following example:
<jboss-web> <replication-config> <replication-trigger>SET_AND_NON_PRIMITIVE_GET</replication-trigger> <replication-granularity>SESSION</replication-granularity> </replication-config> </jboss-web>
There are 4 possible levels of replication of your session data: each one triggers the session replication between nodes in a different way:
Level | Description | Speed |
SET | This is the best option for performance. The session is replicated only if an attributed is explicity modified with setAttribute method. | good |
SET_AND_GET | With this option any attributed that is get/set is marked as dirty even if it’s not written back to the session. This leads to a significant performance degradation. | slow |
SET_AND_NON_ PRIMITIVE_GET |
This is the default option. It works the same as SET_AND_GET except for primitive system types (String, Integer, Long). Since they are immutable objects they are not replicated when a get is issued. | average |
ACCESS | This is the most conservative option. It causes the session to be marked as dirty whenever it is accessed. Since a the session is accessed during each HTTP request, it will be replicated with each request. Note that use of this option can have a significant performance impact, so use it with caution | v.slow |
In the following table you can see a set of commands and whether they make the session dirty.
Command | SET | SET_AND_NP_GET | SET_AND_GET | ACCESS |
HttpSession session; | NO | NO | NO | YES |
String s = (String)session.getAttribute(“x”); | NO | NO | YES | YES |
Person p = (String)session.getAttribute(“p”); | NO | YES | YES | YES |
Person p = (String)session.getAttribute(“p”); session.setAttribute(p); |
YES | YES | YES | YES |