<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE document [
  <!ENTITY project SYSTEM "project.xml">
]>
<document url="loadbalancers.html">

  &project;
<copyright>
   Licensed to the Apache Software Foundation (ASF) under one or more
   contributor license agreements.  See the NOTICE file distributed with
   this work for additional information regarding copyright ownership.
   The ASF licenses this file to You under the Apache License, Version 2.0
   (the "License"); you may not use this file except in compliance with
   the License.  You may obtain a copy of the License at
 
       http://www.apache.org/licenses/LICENSE-2.0
 
   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
</copyright>
<properties>
<title>LoadBalancer HowTo</title>
<author email="mturk@apache.org">Mladen Turk</author>
<date>$Date: 2018-08-22 13:24:46 +0100 (Wed, 22 Aug 2018) $</date>
</properties>
<body>
<section name="Introduction"> 
<br/>
<p>A load balancer is a worker that does not directly communicate with Tomcat.
Instead it is responsible for the management of several "real" workers,
called members or sub workers of the load balancer.</p>
<p>
This management includes:
</p>
<ul>
<li>
Instantiating the workers in the web server.
</li>
<li>
Using the worker's load balancing factor, perform weighted load balancing
(distributing load according to defined strengths of the targets).
</li>
<li>
Keeping requests belonging to the same session executing on the same Tomcat
(session stickyness).
</li>
<li>
Identifying failed Tomcat workers, suspending requests to them and instead
failing over on other workers managed by the load balancer.
</li>
<li>
Providing status and load metrics for the load balancer itself and all
members via the status worker interface.
</li>
<li>
Allowing to dynamically reconfigure load balancing via the status worker
interface.
</li>
</ul>
<p>
Workers managed by the same load balancer worker are load balanced
(based on their configured balancing factors and current request or session load)
and also secured against failure by providing failover to other members of the same
load balancer. So a single Tomcat process death will not "kill" the entire site.
</p>
<p>Some of the features provided by a load balancer are even interesting, when
only working with a single member worker (where load balancing is not possible).</p>

<subsection name="Basic Load Balancer Properties">
<p>A worker is configured as a load balancer by setting its worker <code>type</code>
to <b>lb</b>.
</p>
<p>
The following table specifies some properties used to configure a load balancer worker:
</p>
<ul>
<li><b>balance_workers</b> is a comma separated list of names of the member workers of the
load balancer. These workers are typically of type <b>ajp13</b>. The member workers do
not need to appear in the <code>worker.list</code> property themselves, adding the
load balancer to it suffices.</li>
<li><b>sticky_session</b> specifies whether requests with SESSION ID's should be routed
back to the same Tomcat instance that created the session. You can set sticky_session to
<b>false</b> when Tomcat is using a session manager which can share session data across
multiple instances of Tomcat - or if your application is stateless.
By default sticky_session is set to <b>true</b>.</li>
<li><b>lbfactor</b> can be added to each member worker to configure individual
strengths for the members. A higher <code>lbfactor</code> will lead to more
requests being balanced to that worker. The factors must be given by integers and the
load will be distributed proportional to the factors given. Higher factors lead to
more requests.</li>
</ul>

<source>
  # The load balancer worker balance1 will distribute
  # load to the members worker1 and worker2
  worker.balance1.type=lb
  worker.balance1.balance_workers=worker1, worker2
  worker.worker1.type=ajp13
  worker.worker1.host=myhost1
  worker.worker1.port=8009
  worker.worker2.type=ajp13
  worker.worker1.host=myhost2
  worker.worker1.port=8009
</source>

<warn>
Session stickyness is not implemented using a tracking table for sessions.
Instead each Tomcat instance gets an individual name and adds its name at
the end of the session id. When the load balancer sees a session id, it
finds the name of the Tomcat instance and sends the request via the correct
member worker. For this to work you must set the name of the Tomcat instances
as the value of the <code>jvmRoute</code> attribute in the Engine element of
each Tomcat's server.xml. The name of the Tomcat needs to be equal to the name
of the corresponding load balancer member. In the above example, Tomcat on host
"myhost1" needs <code>jvmRoute="worker1"</code>, Tomcat on host "myhost2"
needs <code>jvmRoute="worker2"</code>.
</warn>

<p>For a complete reference of all load balancer configuration
attributes, please consult the worker <a href="../reference/workers.html">reference</a>.
</p>
</subsection>

<subsection name="Advanced Load Balancer Worker Properties">
<p>The load balancer supports complex topologies and failover configurations.
Using the member attribute <code>distance</code> you can group members.
The load balancer will always send a request to a member of lowest distance.
Only when all of those are broken, it will balance to the members of the
next higher configured distance. This allows to define priorities between
Tomcat instances in different data center locations.
</p>
<p>When working with shared sessions, either by using session replication
or a persisting session manager (e.g. via a database), one often splits
up the Tomcat farm into replication groups. In case of failure of a member,
the load balancer needs to know, which other members share the session.
This is configured using the <code>domain</code> attribute. All workers
with the same domain are assumed to share the sessions.</p>
<p>For maintenance purposes you can tell the load balancer to not
allow any new sessions on some members, or even not use them at all.
This is controlled by the member attribute <code>activation</code>.
The value <b>Active</b> allows normal use of a member, <b>disabled</b>
will not create new sessions on it, but still allow sticky requests,
and <b>stopped</b> will no longer send any requests to the member.
Switching the activation from "active" to "disabled" some time before
maintenance will drain the sessions on the worker and minimize disruption.
Depending on the usage pattern of the application, draining will take from
minutes to hours. Switching the worker to stopped immediately before
maintenance will reduce logging of false errors by mod_jk.</p>
<p>Finally you can also configure hot spare workers by using
<code>activation</code> set to <b>disabled</b> in combination with
the attribute <code>redirect</code> added to the other workers:</p>

<source>
  # The advanced router LB worker
  worker.list=router
  worker.router.type=lb
  worker.router.balance_workers=worker1,worker2

  # Define the first member worker
  worker.worker1.type=ajp13
  worker.worker1.host=myhost1
  worker.worker1.port=8009
  # Define preferred failover node for worker1
  worker.worker1.redirect=worker2

  # Define the second member worker
  worker.worker2.type=ajp13
  worker.worker2.host=myhost2
  worker.worker2.port=8009
  # Disable worker2 for all requests except failover
  worker.worker2.activation=disabled
</source>

<p>
The <code>redirect</code> flag on worker1 tells the load balancer
to redirect the requests to worker2 in case that worker1 has a problem.
In all other cases worker2 will not receive any requests, thus acting
like a hot standby.
</p>

<p>A final note about setting <code>activation</code> to <b>disabled</b>:
The session id coming with a request is send either
as part of the request URL (<code>;jsessionid=...</code>) or via a cookie.
When using bookmarks or browsers that are running since a long time,
it is possible to send a request carrying an old and invalid session id
pointing at a disabled member.
Since the load balancer does not have a list of valid sessions, it will
forward the request to the disabled member. Thus draining takes longer than
expected. To handle such cases, you can add a Servlet filter to your web
application, which checks the request attribute <code>JK_LB_ACTIVATION</code>.
This attribute contains one of the strings "ACT", "DIS" or "STP". If you
detect "DIS" and the session for the request is no longer active, delete the
session cookie and redirect using a self-referential URL. The redirected
request will then no longer carry session information and thus the load
balancer will not send it to the disabled worker. The request attribute
<code>JK_LB_ACTIVATION</code> has been added in version 1.2.32.</p>
</subsection>

<subsection name="Status Worker properties">
<p>
The status worker does not communicate with Tomcat.
Instead it is responsible for the worker management. It is
especially useful when combined with load balancer workers.
</p>
<source>
  # Add the status worker to the worker list
  worker.list=jkstatus
  # Define a 'jkstatus' worker using status
  worker.jkstatus.type=status
</source>
<p>Next thing is to mount the requests to the jkstatus worker. For Apache
HTTP Servers use:</p>
<source>
  # Add the jkstatus mount point
  JkMount /jkmanager/* jkstatus 
</source>
<p>To obtain a higher level of security use the:</p>
<source>
  # Enable the JK manager access from localhost only
 &lt;Location /jkmanager/&gt;
    JkMount jkstatus
    Require ip 127.0.0.1
 &lt;/Location&gt;
</source>

</subsection>

</section>

</body>
</document>
