Liferay 7.4 + Tomcat Unicast-Based Session Replication

This is assumed in the article that Liferay DXP Clustering between 2 nodes is working properly and both nodes can communicate with each other.
 

What is Session Replication?

In simple words, session replication ensures that if a client connection fails over from one server to another the state of the client session is maintained. 

The entire client session data is copied on a replicated instance. However, the session replication operation does not copy the attributes that cannot be serialized in a session and any instance-specific data.

Session replication along with load balancing provides good failover capabilities.

However, since the blog is primarily concerned with configuring session replication, we won't go into the advantages and disadvantages of session replication.

Simplified Architecture Diagram:


 

The below-mentioned steps will be common for both nodes. (All the steps must be followed in both Liferay Nodes).

Step #1 

Go to liferay_home/tomcat-9.x.x/conf file and open the server.xml file Search for the below tag

<Engine name="Catalina" defaultHost="localhost" >

Add the jvmRoute attribue inside it  jvmRoute="workerX" , the modified tag will look like below:

<Engine name="Catalina" defaultHost="localhost" jvmRoute="workerX">

Note: replace X in workerX with the desired node number viz: 1,2 etc

Step #2 

In the server.xml file search for the below tag 

<!--
      <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>
-->

Below this line add the TCP cluster configuration

  <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster" channelSendOptions="8">
        <Manager className="org.apache.catalina.ha.session.DeltaManager"
                                expireSessionsOnShutdown="false"
                                notifyListenersOnReplication="true"/>

       <Channel className="org.apache.catalina.tribes.group.GroupChannel">
       <Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">
       <Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>
       </Sender>

       <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
                                        address="CurrentLiferayServerNodeIP"
                                        port="4000"
                                        autoBind="100"
                                        selectorTimeout="5000"
                                        maxThreads="6"/>

       <Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/>


       <Interceptor className="org.apache.catalina.tribes.group.interceptors.StaticMembershipInterceptor">
       <Member className="org.apache.catalina.tribes.membership.StaticMember"
                          port="4000"
                          securePort="-1"
                          host="AnotherLiferayServerNodeIP"
                          domain="staging-cluster"
                          uniqueId="{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}"/>
       </Interceptor>

       </Channel>

       <Valve className="org.apache.catalina.ha.tcp.ReplicationValve" filter=""/>

       <Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve"/>

        <ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/>
        </Cluster>

Make sure port 4000 is open and is not restricted by a firewall for both nodes. (You can change the port number to other desired TCP port number, if required)

Step #3

Enabling tomcat clustering logs which will be stored in tomcat-9.x.x/logs directory with name ha.yyyy-mm-dd.log

Open the tomcat-9.x.x/conf/logging.properties and modify the below changes for handlers and .handlers

handlers = 1catalina.org.apache.juli.AsyncFileHandler, 2localhost.org.apache.juli.AsyncFileHandler, 3manager.org.apache.juli.AsyncFileHandler, 4host-manager.org.apache.juli.AsyncFileHandler, java.util.logging.ConsoleHandler, 5cluster.org.apache.juli.FileHandler, 6cluster.org.apache.juli.FileHandler

.handlers = 1catalina.org.apache.juli.AsyncFileHandler, java.util.logging.ConsoleHandler, 5cluster.org.apache.juli.FileHandler

Add the below lines at the end of the same file logging.properties to enable INFO logs in log files

5cluster.org.apache.juli.FileHandler.level = INFO
5cluster.org.apache.juli.FileHandler.directory = ${catalina.base}/logs
5cluster.org.apache.juli.FileHandler.prefix = cluster.

6cluster.org.apache.juli.FileHandler.level = INFO
6cluster.org.apache.juli.FileHandler.directory = ${catalina.base}/logs
6cluster.org.apache.juli.FileHandler.prefix = ha.

org.apache.catalina.tribes.MESSAGES.level = INFO
org.apache.catalina.tribes.MESSAGES.handlers = 5cluster.org.apache.juli.FileHandler

org.apache.catalina.tribes.level = INFO
org.apache.catalina.tribes.handlers = 5cluster.org.apache.juli.FileHandler

org.apache.catalina.ha.level = INFO
org.apache.catalina.ha.handlers = 6cluster.org.apache.juli.FileHandler

Save and Exit the file

Step #4

Go to tomcat-9.x.x/webapps/ROOT/WEB-INF/ and open web.xml Inside the <web-app> tag add the <distributable/> tag like below


 

Save and Exit the file.

Step #5

Add the below property to the portal-ext.properties file to enable portlet session replication 

portlet.session.replicate.enabled=true

Save and exit the file

Step #6

Validating the Session Replication

  • Start Liferay Node 1, make sure it is fully UP and running, then start Liferay Node 2.
  • Once both are Up and running, test the session replication functionality by logging into the portal from any node in Browser. 
  • Note down the JSESSION ID post login and the workerX from JSESSION ID cookie. 
  • Shut down the node on which Login happened, in the browser, URL change the IP address and Port to the other node's IP address and Port, keep rest of the URL same. 
  • Hit the Enter button, you will notice that you are still logged in and continue the session on another node as well.

NOTE: In case you are logged out in the above step and shown a URL not found error, that indicates that the session replication is not configured. Check the firewall, logs to debug further.

If you are getting error like the following:

Caused by: java.lang.IllegalArgumentException
'exmapleBean' attribute with type 'com.demo.portlet.example.' cannot be replicated
at org.jboss.as.web.session.ClusteredSession.setAttribute(ClusteredSession.java:824)
at org.apache.catalina.session.StandardSessionFacade.setAttribute(StandardSessionFacade.java:130)
at com.liferay.portlet.PortletSessionImpl.setAttribute(PortletSessionImpl.java:200)

 Make sure all session objects and their attributes/variables are made Serializable.