Sunday, October 14, 2012

How to Load Balancing and Failover with Apache/Tomcat


Front-ending Apache Tomcat with Apache Web Server is sometimes thought to improve performance. However, performance of Tomcat standalone has already been known to be very good. So why add  Apache web server in front of it? – the answer is scalability and maintenance. Front-ending Tomcat with such web servers allows you to add more instances in case of increased load and also bring down instances for maintenance/upgrades.

These solutions provides high scalability, high availability, and good load balancing capabilities that are comparable with any other software solution. 
In order to get this done, you’ll need Apache 2.2, Tomcat 6 or 7, and the MOD_JK connector library.

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.

  1. Install Apache 2.2. With this configuration.


  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 file, name it workers.properties 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 6 or 7 installation ZIP archive to three different directories, as we’re going to load balance three instances of Tomcat.


  6. Replace 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.



  7. Start (or Restart) Apache 2.2
  8. Start each instance of Tomcat (use the startup.bat script in the Tomcat /bin directory) – you should see no errors.
  9. Verify each Tomcat is working by opening a browser window to each Tomcat instance – if you’ve followed my instructions, the links are: 
  10. If Tomcat started correctly, start Apache 2.2. You should be able to access the Tomcat example pages via the following URL: 
  11. You’re done. Using my configuration, you can access a page to control the JK connector here:

    I’d recommending hiding and protecting this should you want to put this configuration into production. 

  12. 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.
  13. Now lets deploy, our web service that we just made before. Hrms.war
  14. Just put the war into TOMCAT_HOME\webapps.
  15. Start (or Restart) Apache 2.2
  16. Start each instance of Tomcat (use the startup.bat script in the Tomcat /bin directory) – you should see no errors.
  17. Verify each tomcat instance is working : 

  18. Now open the apache instance via the following URL : 




Conclusions

We have succeeded in creating a solution that provides high scalability, high availability, and good load balancing capabilities for our web services. For every request to our Apache Server, the load will be divided between the node server available (Tomcat instance). Even if one or more node server is down, as long as there is one node still running, the request will be responded by Apache. That is the true meaning of High Scalability and High Availability.

No comments:

Post a Comment