I've been struggling to make load balancing and clustering working using these combination:
- Apache HTTP Server 2.2.21 (httpd-2.2.21-win32-x86-openssl-0.9.8r) using mod_proxy_ajp with sticky session enabled.
- Apache Tomcat 7.0.23 (apache-tomcat-7.0.23-windows-x64)
- JDK 7 update 2 (jdk-7u2-windows-x64)
- Windows 7 64 Bit
- Spring 3.1
Some links that I've read:
- Tomcat Clustering - A Step By Step Guide
- Setting up Tomcat Cluster for Session Replication
- Understanding Tomcat Context Containers
- Clustering/Session Replication HOW-TO
This is my configuration:
httpd.conf
# Required Modules
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_ajp_module modules/mod_proxy_ajp.so
LoadModule proxy_balancer_module modules/mod_proxy_balancer.so
LoadModule status_module modules/mod_status.so
# Reverse Proxy
<Proxy balancer://mybalancer>
BalancerMember ajp://localhost:8301 route=s1
BalancerMember ajp://localhost:8302 route=s2
BalancerMember ajp://localhost:8303 route=s3
</Proxy>
ProxyPass / balancer://mybalancer/ stickysession=JSESSIONID|jsessionid
# Forward Proxy
ProxyRequests Off
<Proxy *>
Order deny,allow
Deny from none
Allow from localhost
</Proxy>
# Balancer-manager, for monitoring
<Location /balancer-manager>
SetHandler balancer-manager
Order deny,allow
Deny from none
Allow from localhost
</Location>
server.xml for each tomcat (difference only in port number)
<Server port="8001" shutdown="SHUTDOWN">
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
<Listener className="org.apache.catalina.core.JasperListener" />
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
<Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
<GlobalNamingResources>
<Resource name="UserDatabase" auth="Container"
type="org.apache.catalina.UserDatabase" description="User database that can be updated and saved"
factory="org.apache.catalina.users.MemoryUserDatabaseFactory" pathname="conf/tomcat-users.xml" />
</GlobalNamingResources>
<Service name="Catalina">
<Connector port="8101" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8201" />
<Connector port="8301" protocol="AJP/1.3" redirectPort="8201" />
<Engine name="Catalina" defaultHost="localhost" jvmRoute="s1">
<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">
<Membership className="org.apache.catalina.tribes.membership.McastService"
address="228.0.0.4" port="45564" frequency="500" dropTime="3000" />
<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="auto" 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.MessageDispatch15Interceptor" />
</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.JvmRouteSessionIDBinderListener" />
<ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener" />
</Cluster>
<Realm className="org.apache.catalina.realm.LockOutRealm">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase" />
</Realm>
<Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true">
<Valve className="org.apache.catalina.valves.AccessLogValve"
directory="logs" prefix="localhost_access_log." suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
</Host>
</Engine>
</Service>
</Server>
port numbering (make sure no instances use the same port if deployed in the same server)
- 80 -> Apache HTTP Server Port
- 80xx -> Tomcat Server SHUTDOWN Port
- 81xx -> Tomcat Connector Port (HTTP)
- 82xx -> Tomcat SSL Redirect Port
- 83xx -> Tomcat AJP Port
- 40xx -> Tomcat tcp receive port for NioReceiver
Spring Controller
@Controller
@RequestMapping("/login")
public class LoginController {
@RequestMapping(method=RequestMethod.GET)
public String show(@ModelAttribute("user") User user, HttpServletRequest request) {
user.setUsername("YUSUF");
HttpSession session = request.getSession();
Integer tambah = (Integer) session.getAttribute("tambah");
if(tambah == null) tambah = new Integer(1);
else tambah = new Integer(tambah.intValue() + 1);
session.setAttribute("tambah", tambah);
return "login";
}
}
login.jsp
<div class="mainFooter">
Tambah = ${sessionScope.tambah}
<br>
ID = ${pageContext.session.id}
</div>
So far the load balancing part is working, but the session replication is not.
Basically what I want is if I keep pressing refresh on the login page, the variable "tambah" will be incremented and kept in the session. And if the current tomcat node is down, the session will be replicated to the next tomcat node and the data is not gone. But this is what happening:
Login Screen:
Tambah = 39
ID = C1D59C8CA5D10EB98C1DE08AC618204D.s1
I take the tomcat1 down and keep tomcat2 and tomcat3 running, here is the Login Screen:
Tambah = 1
ID = A83KJFO38FK30FJDL40FLREI39FKDKGD.s2
It seems the failover is not working, the session is not replicated, and the application created a new session. Can someone help point me to the right direction?
Thanks
EDIT: I've solved the question, thanks to @Shane Madden, this is the changes I've made:
<Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
address="auto" port="4000" autoBind="100" selectorTimeout="5000" maxThreads="6" />
For each Tomcat, the tcp receiver port must be different (if running on the same server), for example use 4001, 4002, 4003, etc.