Начало J2EE Клъстериране на Apache Tomcat

ВАЖНО

Приключи есенния семинар на BGOUG. Повече информация за моята лекция по време на семинара има в Събития


Лицензирано под:
Creative Commons License
Клъстериране на Apache Tomcat
Сряда, 19 Ноември 2008 17:57
Една от последните инсталации, които правих представляваше J2EE приложение върху Tomcat контейнер. Приложението ползваше за база данни Oracle Database 10g. Тъй като се заиграх с базата и я настроих в Real Application Cluster, някак не вървеше самото приложение да не използва механизъм за load-balancing при положение, че за него бяха заделени два физически сървъра.

След известно обмисляне, реших че схемата на разполагане ще изглежда така:

Apache Tomcat Cluster

Tomcat се инсталира върху двете машини и се насочва към клъстера на базата данни, а пред двата Tomcat-а се поставя един Apache HTTP Server, който балансира потребителските връзки между двата контейнера.

След малко ровене в документацията на HTTP Server и Tomcat, открих и нужните промени в конфигурацията, които трябваше да се направят. И така...

Конфигурация на Apache HTTP Server

Първото, което трябва да направим е да добавим The Apache Tomcat Connector към HTTP Server-a или както е по-известен - mod_jk. Последната версия на connector-а може да се свали от http://tomcat.apache.org/connectors-doc, като Apache предоставят готови binary-та за linux, win32, win64 (моят случай), solaris и други. Сваления файл поставяме в директорията с модули на HTTP Server, която при мен е C:\apache\modules.

За да може mod_jk да работи коректно той се нуждае от конфигурационен файл, в който да опишем хостовете, върху които работи Tomcat. Този файл кръщаваме workers.properties, разполагаме го в /apache/conf и той изглежда така:

worker.list=worker1, worker2, loadbalancer

# Настройки за хост 1

worker.worker1.type=ajp13
worker.worker1.host=ip_address_host_1
worker.worker1.port=8009

# Настройки за хост 2

worker.worker2.type=ajp13
worker.worker2.host=ip_address_host_2
worker.worker2.port=8009

# Настройки на load balancer-a

worker.loadbalancer.type=lb
worker.loadbalancer.balance_workers=worker1, worker2
worker.loadbalancer.sticky_session=1

Както се вижда в него описваме отделните машини, които ще извършват същинската работа (workers), а също така задаваме и конфигуриращи параметри на механизма за баланс. Единствения по-особен параметър, който използвам в случая е sticky_session. С него задаваме, че след първоначалното свързване на един потребител към някой от двата хоста, всички негови заявки трябва да се насочват винаги към този първоначално избран хост, докато приключи потребителската сесия.

След като сме описали по този начин хостовете и поведението на баланс, остава да редактираме главния конфигурационен файл на HTTP Server-а, а именно httpd.conf. За моята инсталация той беше разположен в C:\apache\conf, а редактирането му се изчерпва с добавянето на следните редове в него:

# Зареждане на mod_jk
LoadModule    jk_module  modules/mod_jk.so

# Място на конфигурационния файл workers.properties
JkWorkersFile "C:/apache/conf/workers.properties"

# Път за генериране на лог файлове
JkLogFile     "C:/apache/logs/mod_jk.log"

# Ниво на информацията в логовете [debug/error/info]
JkLogLevel    error

# Контекст, който да се обслужва от load balancer-а
JkMount  /servlets-examples/servlet/* loadbalancer

След записа на файла рестартираме процеса на HTTP Server-а и с това неговата конфигурация приключва.

Конфигурация на Tomcat

Конфигурацията на Tomcat се оказа още по-лесна. Трябва да редактираме единствено server.xml файла му, намиращ се при мен в c:\tomcat\conf. Редакциите, които се налага да извършим са следните:

Променяме HTTP порта, на който слуша сървъра от 8080 на 80.

<!-- Define a non-SSL HTTP/1.1 Connector on port 8080 -->
    <Connector port="80" maxHttpHeaderSize="8192"
               maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
               enableLookups="false" redirectPort="8443" acceptCount="100"
               connectionTimeout="20000" disableUploadTimeout="true" />

Уверяваме се, че секцията на AJP конектора не е коментирана и че той е настроен на същия порт като описания във workers.properties:

    <!-- Define an AJP 1.3 Connector on port 8009 -->
    <Connector port="8009"
               enableLookups="false" redirectPort="8443" protocol="AJP/1.3" />

Намираме активната секция Engine name и добавяме jvmRoute директива към нея, съдържаща името на съответния worker за дадения хост:

<!-- Define the top level container in our container hierarchy -->
<Engine name="Catalina" defaultHost="localhost" jvmRoute="worker1">

Тук трябва да сме особено внимателни, тъй като точно над тази секция има друга съдържаща Engine и jvmRoute, но заградена в коментари (неактивна). Ако решим да ползваме нея вместо да дописваме в тази, трябва да махнем коментарите, да поправим стойността на jvmRoute и да поставим долната, активна секция в коментари.

Същите промени трябва да направим и в server.xml файла на другия хост, като единствената разлика е стойността на jvmRoute, която на втория хост ще бъде worker2.

Конфигуриране на примерен сървлет

За да мога да наблюдавам работата на load balancer-а, редактирах един от примерните сървлети, които стандартно са включени в инсталацията на Tomcat. Преправих SessionExample.java намиращ се в C:\tomcat\webapps\servlets-examples\WEB-INF\classes, като заместих кода му по следния начин и в допълнение върху първата машина сложих TITLE атрибута да бъде HOST 1, а върху втората HOST 2:

import java.io.*;
import java.text.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class SessionExample extends HttpServlet {

    ResourceBundle rb = ResourceBundle.getBundle("LocalStrings");
   
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
        response.setContentType("text/html");

        PrintWriter out = response.getWriter();
        out.println("<html>");
        out.println("<body bgcolor=\"white\">");
        out.println("<head>");

        String title = rb.getString("sessions.title");
        out.println("<title>" + title + "</title>");
        out.println("</head>");
        out.println("<body>");

        out.println("<h3>HOST 1</h3>");

        HttpSession session = request.getSession(true);
        out.println(rb.getString("sessions.id") + " " + session.getId());
        out.println("<br>");
        out.println(rb.getString("sessions.created") + " ");
        out.println(new Date(session.getCreationTime()) + "<br>");
        out.println(rb.getString("sessions.lastaccessed") + " ");
        out.println(new Date(session.getLastAccessedTime()));
       
        out.println("</body>");
        out.println("</html>");
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
        doGet(request, response);
    }

}

Сървлета компилирах и върху двата Tomcat-а, като използвах командата

C:\tomcat\webapps\servlets-examples\WEB-INF\classes>javac -classpath "c:\tomcat\common\lib\servlet-api.jar" SessionExample.java

Тестване на конфигурацията

След стартирането на двата Tomcat-а чрез c:\tomcat\bin\startup.bat, можем да тестваме load balancer-a като се обърнем към него по HTTP и поискаме SessionExample сървлета. При мен хоста с HTTP Server-a беше кръстен cluster-apache, а извикването на URL-то http://cluster-apache/servlets-examples/servlet/SessionExample върна следния резултат:

Tomcat Cluster Test

Интересно е да обърнем внимание на добавения към Session ID-то маркер „worker2”. Той е там заради sticky_session опцията. По него HTTP Server-ът се ориентира към кой хост трябва да препраща заявките на потребителя, който вече има изградена сесия. Ако опитаме да презаредим страницата ще видим, че всеки опит ще ни води до HOST 2 (или HOST 1, ако сме били насочени първоначално към него). От друга страна, ако зададем стойност 0 на sticky_session, всяко презареждане на страницата ще ни препраща последователно между двата хоста.

Коментари

Име
URL
Код   
Запис
 

НОВА КНИГА

Oracle Database Security Book
(c) 2004-2008 Николай Манчев. Освен ако изрично не е споменато нещо друго, всички материали публикувани тук се разпространяват под Creative Commons Attribution License. Материали, коментари и изображения, които не са създадени и подписани от мен са собственост на съответните им автори.