Source Allies Logo

Sharing Our Passion for Technology

& Continuous Learning

<   Back to Blog

Apache/Tomcat with Failover and Load Balancing in 20 minutes or less...

In order to get this done, you'll need Apache, Tomcat, and the MOD_JK connector library. I've included the Windows binaries below, as I'm writing this blog post from a Windows machine. Feel free to swap out these downloads with whatever RPM, YAST, APT, etc. commands you want. Also, you'll need to have a Java JDK installed, and your JAVA_HOME pointing to it. Make sure your path contains %JAVA_HOME%/bin.

Download and Apache from here: http://apache.mirrors.tds.net/httpd/binaries/win32/apache_2.2.14-win32-x86-no_ssl.msi

Download and extract Tomcat from here: http://mirrors.axint.net/apache/tomcat/tomcat-6/v6.0.20/bin/apache-tomcat-6.0.20.zip

Download MOD_JK from here: https://archive.apache.org/dist/tomcat/tomcat-connectors/jk/binaries/win32/jk-1.2.28/mod_jk-1.2.28-httpd-2.2.3.so

  1. Install Apache.
  2. Copy the mod_jk-1.2.28-httpd-2.2.3.so file to your apache/modules directory.
  3. Add the following to your apache/conf/httpd.conf
    LoadModule jk_module modules/mod_jk-1.2.28-httpd-2.2.3.so
    
    JkWorkersFile     conf/workers.properties
    JkLogFile       logs/jk.log
    JkLogLevel       debug
    
    JkMount        /*      router
    JkMount        /jk_status  status
    
  4. Create a workers.properties file in your apache/conf directory. The file should contain the following:
    worker.list=router,status
    
    worker.worker1.port=8109
    worker.worker1.host=localhost
    worker.worker1.type=ajp13
    worker.worker1.lbfactor=1
    worker.worker1.local_worker=1
    worker.worker1.sticky_session=0
    
    worker.worker2.port=8209
    worker.worker2.host=localhost
    worker.worker2.type=ajp13
    worker.worker2.lbfactor=1
    worker.worker2.local_worker=0
    worker.worker2.sticky_session=0
    
    worker.worker3.port=8309
    worker.worker3.host=localhost
    worker.worker3.type=ajp13
    worker.worker3.lbfactor=1
    worker.worker3.local_worker=0
    worker.worker3.sticky_session=0
    
    worker.router.type=lb
    worker.router.balanced_workers=worker1,worker2,worker3
    worker.router.local_worker_only=1
    
    worker.status.type=status
    
  5. Extract the Tomcat installation ZIP archive to three different directories, as we're going to load balance three instances of Tomcat. You'll be replacing the server.xml file in each of the Tomcat conf/ directories with the following:
    <Server port="8100" shutdown="SHUTDOWN">
    
    <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="8180" protocol="HTTP/1.1"
       connectionTimeout="20000"
       redirectPort="8443" />
    
      <!-- Define an AJP 1.3 Connector -->
      <Connector port="8109" protocol="AJP/1.3" redirectPort="8443" />
    
      <Engine name="Catalina" defaultHost="localhost" jvmRoute="jvm1">
       <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
         resourceName="UserDatabase"/>
    
       <Host name="localhost" appBase="webapps"
         unpackWARs="true" autoDeploy="true"
         xmlValidation="false" xmlNamespaceAware="false">
    
         <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"/>
    
            <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
             address="auto"
             port="4000"
             autoBind="100"
             selectorTimeout="5000"
             maxThreads="6"/>
    
            <Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">
             <Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>
            </Sender>
    
            <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"/>
    
          <Deployer className="org.apache.catalina.ha.deploy.FarmWarDeployer"
            tempDir="/tmp/war-temp/"
            deployDir="/tmp/war-deploy/"
            watchDir="/tmp/war-listen/"
            watchEnabled="false"/>
    
          <ClusterListener className="org.apache.catalina.ha.session.JvmRouteSessionIDBinderListener"/>
          <ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/>
         </Cluster>
       </Host>
      </Engine>
    </Service>
    </Server>
    

    Looking into the XML above, there are three ports we're concerned with: 8100, 8180, and 8109. These are the server, HTTP, and AJP13 ports, respectively. We'll need each instance of Tomcat to run on it's own ports. So, you can use this file as is in your first folder containing Tomcat, however, you'll need to change the port numbers to: 8200, 8280, and 8209 for you 2nd installation. The third installation will use the ports, 8300, 8380, and 8309.

    <Server port="8100" shutdown="SHUTDOWN">
    
    <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="8180" protocol="HTTP/1.1"
          connectionTimeout="20000"
          redirectPort="8443" />
    
       <!-- Define an AJP 1.3 Connector -->
       <Connector port="8109" protocol="AJP/1.3" redirectPort="8443" />
    
       <Engine name="Catalina" defaultHost="localhost" jvmRoute="jvm1">
          <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
             resourceName="UserDatabase"/>
    
          <Host name="localhost"  appBase="webapps"
             unpackWARs="true" autoDeploy="true"
             xmlValidation="false" xmlNamespaceAware="false">
    
             <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"/>
    
                   <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
                      address="auto"
                      port="4000"
                      autoBind="100"
                      selectorTimeout="5000"
                      maxThreads="6"/>
    
                   <Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">
                      <Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>
                   </Sender>
    
                   <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"/>
    
                <Deployer className="org.apache.catalina.ha.deploy.FarmWarDeployer"
                   tempDir="/tmp/war-temp/"
                   deployDir="/tmp/war-deploy/"
                   watchDir="/tmp/war-listen/"
                   watchEnabled="false"/>
    
                <ClusterListener className="org.apache.catalina.ha.session.JvmRouteSessionIDBinderListener"/>
                <ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/>
             </Cluster>
          </Host>
       </Engine>
    </Service>
    </Server>
    

    Looking into the XML above, there are three ports we're concerned with: 8100, 8180, and 8109. These are the server, HTTP, and AJP13 ports, respectively. We'll need each instance of Tomcat to run on it's own ports. So, you can use this file as is in your first folder containing Tomcat, however, you'll need to change the port numbers to: 8200, 8280, and 8209 for you 2nd installation. The third installation will use the ports, 8300, 8380, and 8309.

  6. Start (or Restart) Apache
  7. Start each instance of Tomcat (use the startup script in the Tomcat /bin directory) - you should see no errors.
  8. Verify each Tomcat is working by opening a browser window to each Tomcat instance - if you've followed my instructions, the links are: http://localhost:8180/examples/servlets/, http://localhost:8280/examples/servlets/, and http://localhost:8380/examples/servlets/.
  9. If Tomcat started correctly, start Apache. You should be able to access the Tomcat example pages via the following URL: http://localhost/examples/servlets/
  10. You're done. Using my configuration, you can access a page to control the JK connector here: http://localhost/jk_status, I'd recommending hiding and protecting this should you want to put this configuration into production. Try experimenting with the configuration by stopping instance of Tomcat... as long as one instance of Tomcat is running, you should be able to see the examples.