оригинал статьи тут
Эта заметка посвящена тому, как развернуть кластер из Tomcat серверов и настроить простую балансировку нагрузки для кластера.
Начнем сначала. Зачем нужен кластер? Обычным приложениям, которые обслуживают относительно небольшой поток пользователей и к которым не предъявляются серьезные требования со стороны бизнеса, кластер, возможно и не пригодится.
Совсем по-другому обстоят дела с теми приложениями, которые являются ключевыми для функционирования бизнеса, к примеру, приложения которые обрабатывают биллинговые операции. Если “ляжет” сервис такого рода, то некоторый бизнес-процесс, тоже перестанет функционировать.
Назначение кластера: повысить общую устойчивость и масштабируемость системы. Устойчивость системы повышается за счет того, что запросы пользователей обрабатываются не одним сервером, а сразу несколькими. Поэтому в случае сбоя одного из серверов, или при необходимости перезагрузки, сервис будет продолжать нормально функционировать.
Что касается масштабируемости: увеличить производительность системы можно за счет расширения кластера. То есть, если два сервера не справляются с задачей, то довольно просто поставить третий, подключить его к кластеру и увеличить производительность системы на 50%. Надо заметить, что расширять кластер таким образом бесконечно нельзя, есть некоторые сдерживающие факторы: к примеру, объем трафика, который необходим для передачи данных сессий между нодами кластера. Об этом чуть позже.
В этой заметке приведен рецепт развертывания кластера из нескольких Tomcat серверов на одной рабочей станции. Необходимые компоненты: дистрибутив Tomcat 5.0.28 (можно 5.5.xx — процесс ничем не отличается), Apache HTTP server 2.0.59 + mod_jk.
Шаг 1. Разворачиваем первый Tomcat.
Выберите директорию для установки Tomcat серверов кластера. Разархивируйте первый дистрибутив. Запустите сервер и убедитесь, что все работает. Теперь у нас есть установленный Tomcat, на котором уже запущено несколько приложений. Работу кластера мы будем тестировать на примере приложения jsp-examples из дистрибутива.
Шаг второй. Настроим Apache HTTP + mod_jk.
Первый вопрос, который возникает на этом шаге: зачем _одному_ Tomcat’у (ведь кластера еще нет) нужен еще и Apache, если Tomcat и сам неплохо справляется с обработкой HTTP запросов? Ответ, который предоставляют разработчики, довольно неожиданный: Tomcat действительно неплохо работает с HTTP запросами, но в реальном мире, где на сервер сыплются тонны некорректных запросов и оборванных соединений — Apache справляется куда лучше. Второй нюанс: Tomcat все же написан на Java. Цена кроссплатформенности Java — это невозможность оптимизировать код под конкретную платформу. И снова-таки Apache лишен этого недостатка.
Таким образом в нашей архитектуре Apache HTTP server будет выступать в качестве “фильта” между пользователями и Tomcat серверами. На следующих шагах Apache будет выполнять еще и функцию распределителя нагрузки.
1. Установите Apache.
2. Запустите Apache и убедитесь что он работает: зайдите на http://localhost — там должна быть стартовая страничка Apache.
3. Скачайте mod_jk необходимой версии, переименуйте файл в mod_jk.so и поместите его в папку modules.
4. В файл httpd.conf добавляем такие строки:
LoadModule jk_module modules/mod_jk.so
# Path to workers.properties
JkWorkersFile conf/workers.properties
# Path to jk logs
JkLogFile logs/mod_jk.log
# Jk log level [debug/error/info]
JkLogLevel info
# Jk log format
JkLogStampFormat «[%a %b %d %H:%M:%S %Y] »
# JkOptions for forwarding
JkOptions +ForwardKeySize +ForwardURICompat -ForwardDirectories
# JkRequestLogFormat set the request format
JkRequestLogFormat «%w %V %T»
JkMount /jsp-examples alpha
JkMount /jsp-examples/* alpha
5. В папке conf создаем файл workers.properties. Содержимое этого файла будет таким:
workers.tomcat_home=C:/apps/cluster/alpha
workers.java_home=C:/apps/jdk1.5.0_11
worker.list=alpha
worker.alpha.port=8009
worker.alpha.host=localhost
worker.alpha.type=ajp13
Естественно, вместо указанных путей укажите собственные.
Теперь поправим конфигурацию Tomcat:
6. Убедимся, что AJP коннектор настроен именно так:
protocol="AJP/1.3" />
7. Перезагружаем Tomcat и Apache. Убедимся, что все работает так как надо:
Если в браузере набрать http://localhost/jsp-examples/ — должна отобразиться страница с примерами jsp из дистрибутива Tomcat. Если страничка есть — значит все работает.
Поздравляю, теперь обработкой HTTP запросов занимается Apache.
Шаг третий. Добавим в кластер еще один сервер и настроим балансировку нагрузки.
1. Разархивируем еще один дистрибутив Tomcat.
2. В server.xml поправим конфигурацию так, чтобы не было конфликтов портов:
…
enableLookups="false" redirectPort="8443" acceptCount="100"
debug="0" connectionTimeout="20000"
disableUploadTimeout="true" />
…
protocol="AJP/1.3" />
…
3. Поправим workers.properties, чтобы нагрузка баллансировалась между двумя нодами:
workers.tomcat_home=C:/apps/cluster/alpha
workers.java_home=C:/apps/jdk1.5.0_11
worker.list=balancer
worker.alpha.port=8009
worker.alpha.host=localhost
worker.alpha.type=ajp13
worker.alpha.lbfactor=1
worker.bravo.port=8010
worker.bravo.host=localhost
worker.bravo.type=ajp13
worker.bravo.lbfactor=1
worker.balancer.type=lb
worker.balancer.balance_workers=alpha,bravo
4. Поправим httpd.conf:
JkMount /jsp-examples balancer
JkMount /jsp-examples/* balancer
5. Чтобы понимать, на какую ноду мы попали, поправим файлы webapps\jsp-examples\index.html в обеих нодах. В первой ноде добавим строку “Alpha”, а во второй “Bravo”. Теперь будет видно, какой из серверов в действительно обрабатывает запрос.
5. Запустим только что установленный Tomcat (теперь работает две ноды).
6. Перезапустим Apache.
7. Набираем в браузере http://localhost/jsp-examples/
8. Смотрим, какая нода обрабатывает наш запрос и останавливаем соответствующий Tomcat.
9. Обновляем страницу в браузере — теперь запрос обрабатывает другая нода (и никаких 404
)
Только что мы проимитировали ситуацию, когда одна из нод кластера перестала функционировать. Как видно — переключение на работающую ноду прошло абсолютно прозрачно для пользователя (если бы не надписи “Alpha” и “Bravo” пользователь даже не узнал-бы о переключении).
Шаг четвертый. Настаиваем репликацию сессий.
При существующей архитектуре, данные сессии пользователя при крахе ноды будут утеряны. Это далеко не всегда допустимо. На этом шаге мы настроим репликацию сессий: Tomcat ноды будут обмениваться данными о сессиях. Таким образом, если одна из нод упадет — копии сессии останутся на остальных нодах и пользователь сможет продолжить нормально работать.
На данном этапе элементы кластера не знают о существовании друг-друга. Фактически, все что надо сделать для репликации сессий это “познакомить” их.
К сожалению, в комплекте примеров jsp нет страниц, которые демонстрируют использование сессий. Поэтому прийдется немного повозиться, чтобы получить возможность проверить репликацию.
Создадим страничку session.jsp с таким содержанием:
<%@ page contentType="text/html;charset=windows-1251" %>
<%="Node: " + getServletContext().getRealPath(".") + "
» %>
<%
if (session.getAttribute("sessionObj") == null) {
out.println("No session. Setting sessionObj to \"alpha\"");
session.setAttribute("sessionObj", "alpha");
} else {
out.print("sessionObj " + session.getAttribute("sessionObj"));
}
%>
Для ноды bravo создадим точно такую же страницу, только вместо alpha будем устанавливать строку “bravo”.
1. Убедимся, что сейчас репликация сессий не работает:
1.1. Зайдем на http://localhost/jsp-examples/session.jsp, заметим, какая нода обработала запрос.
1.2. Остановим соответствующую ноду.
1.3. Обновим в браузере страницу: как видно, вторая нода понятия не имеет о том, какие объекты добавляла в сессию первая нода.
1.4. Остановим обе ноды.
Теперь, собственно, репликация.
1. В server.xml для ноды alpha прописываем
2. В server.xml для ноды bravo прописываем
2. Раскомментируем блок Cluster нод alpha и bravo
3. В блоке Cluster ноды alpha устанавливаем
mcastAddr="228.0.0.4"
mcastBindAddress="127.0.0.1"
mcastPort="45564"
mcastFrequency="500"
mcastDropTime="3000"/>
tcpListenAddress="auto"
tcpListenPort="4001"
tcpSelectorTimeout="100"
tcpThreadCount="6"/>
4. Аналогично для bravo
mcastAddr="228.0.0.4"
mcastBindAddress="127.0.0.1"
mcastPort="45564"
mcastFrequency="500"
mcastDropTime="3000"/>
tcpListenAddress="auto"
tcpListenPort="4002"
tcpSelectorTimeout="100"
tcpThreadCount="6"/>
5. В web.xml приложения jsp-examples обеих нод добавляем параметр
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.4">
JSP 2.0 Examples.
…
…
5. Повторяем тест на репликацию описанный выше. Теперь, при переходе между нодами, данные из сессии пользователя не будут утеряны.
Заключение.
В этой заметке описан способ настройки кластера Tomcat серверов. Мы использовали HTTP сервер Apache для обработки HTTP запросов и для баллансировки нагрузки между серверами.
Для того чтобы добавить в кластер новую ноду необходимо сделать следующее:
1. Разархивировать дистрибутив tomcat.
2. В server.xml указать
тут xxxx — значение порта, которое не будет конфликтовать с другими портами кластера
3. Поправляем http коннектор
enableLookups="false" redirectPort="8443" acceptCount="100"
debug="0" connectionTimeout="20000"
disableUploadTimeout="true" />
тут xxxx — значение порта, которое не будет конфликтовать с другими портами кластера
4. Указать параметры AJP коннектора:
protocol="AJP/1.3" />
yyyy — аналогично
5. Указать значение jvmRoute для Catalina
значение jvmRoute должно быть уникальным для каждой ноды кластера.
6. Раскомментировать блок Cluster и в нем настроить
mcastAddr="228.0.0.4"
mcastBindAddress="127.0.0.1"
mcastPort="45564"
mcastFrequency="500"
mcastDropTime="3000"/>
tcpListenAddress="auto"
tcpListenPort="zzzz"
tcpSelectorTimeout="100"
tcpThreadCount="6"/>
zzzz — аналогично предыдущим.
В workers.properties добавить описание worker’a для новой ноды
worker.myNextNode.port=yyyy
worker.myNextNode.host=localhost
worker.myNextNode.type=ajp13
worker.myNextNode.lbfactor=1
…
worker.balancer.balance_workers=alpha,bravo,myNextNode
В результате мы получили кластер с баллансировщиком нагрузки, который относительно легко расширяется и который умеет передавать данные сессий между нодами.
Лично мне не удалость настроить для кластера Tomcat серверов Deployment Farm — замечательная возможность кластера, которая позволяет поставлять обновленное приложение одновременно на весь кластер.
Второй нюанс — кластером нельзя управлять динамически: поменяв workers.properties необходимо перезагрузить Apache.
А в остальном — довольно неплохо. Счастливой кластеризации.