'tomcat'에 해당되는 글 5

  1. 2016.07.20 centos tomcat redis3 clustering connect
  2. 2008.01.28 PippoPorxy
  3. 2008.01.28 Tomcat 5에서 클러스터링과 로드밸렁싱-실전적용
  4. 2008.01.28 Tomcat 5에서의 클러스터링과 로드 밸런싱, Part 2
  5. 2008.01.28 Tomcat 5에서의 클러스터링과 로드 밸런싱, Part 1

centos tomcat redis3 clustering connect

tomcat - redis culster - session - replication


1. redis cluster


install centos 7


yum update -y


sudo systemctl set-default multi-user.target


#selinux off


sudo vi /etc/sysconfig/selinux

#SELINUX=enforcing 을 SELINUX=disabled 로 변경후 저장한다.

SELINUX=disabled



sudo yum install -y gcc epel-release java-1.8.0-openjdk lrzsz telnet htop wget  automake cronie network-tools ruby g++ make automake autoconf curl-devel openssl-devel zlib-devel

sudo yum install -y  ruby-rdoc ruby-devel rubygems tcl

gem install redis


sudo firewall-cmd --permanent --zone=public --add-rich-rule="rule family="ipv4"  source address="10.0.0.0/24"  accept"


sudo firewall-cmd --reload

sudo firewall-cmd --list-all


sudo hostnamectl set-hostname drawhanredis1


hostname


sudo vi /etc/hosts

10.0.0.151    drawhanredis1

10.0.0.152    drawhanredis2

10.0.0.153    drawhanredis3


10.0.0.161    drawhanweb1

10.0.0.162    drawhanweb2

10.0.0.163    drawhanwas1

10.0.0.164    drawhanwas2

10.0.0.165    drawhanmobile1

10.0.0.166    drawhanmobile2

10.0.0.167    drawhanmobile3

10.0.0.168    drawhanwcms1

10.0.0.169    drawhanwcms2


mkdir ~/src ; cd ~/src; wget hdrawhan://download.redis.io/releases/redis-3.2.1.tar.gz ; tar xvzf redis-3.2.1.tar.gz

make test; make ; make install;


~/src/redis-3.2.1/utils/install_server.sh


src/redis-trib.rb create --replicas 1 172.16.0.151:7000 172.16.0.152:7001 172.16.0.153:7000 172.16.0.151:7001 172.16.0.152:7000 172.16.0.153:7001



2. apache 설치


install centos 7


yum update -y


sudo yum install -y gcc epel-release java-1.8.0-openjdk lrzsz telnet htop wget automake cronie network-tools g++ make automake autoconf curl-devel openssl-devel zlib-devel


sudo systemctl set-default multi-user.target


#selinux off


sudo vi /etc/sysconfig/selinux

#SELINUX=enforcing 을 SELINUX=disabled 로 변경후 저장한다.

SELINUX=disabled


sudo yum install -y hdrawhand hdrawhand-devel


#mod_ajp_proxy balancer configure

sudo vi /etc/hdrawhand/conf.d/virtual.conf


nameVirtualHost *:80

<Proxy balancer://drawhanCluster>

        Order deny,allow

        Allow from all


        BalancerMember ajp://drawhanwas1:9002

        BalancerMember ajp://drawhanwas2:9002

        ProxySet lbmethod=bytraffic

</Proxy>


<VirtualHost *:80>

        ServerName drawhanwas1


        #ProxyPreserveHost On


        ProxyPass / balancer://drawhanCluster/

        ProxyPassReverse / balancer://drawhanCluster/


        #ProxyPass / ajp://drawhanwas1:9002/

        #ProxyPassReverse / ajp://drawhanwas1:9002/

</VirtualHost>



3.tomcat 설치


install centos 7


yum update -y


sudo yum install -y gcc epel-release java-1.8.0-openjdk lrzsz telnet htop wget automake cronie network-tools g++ make automake autoconf curl-devel openssl-devel zlib-devel


sudo systemctl set-default multi-user.target


#selinux off


sudo vi /etc/sysconfig/selinux

#SELINUX=enforcing 을 SELINUX=disabled 로 변경후 저장한다.

SELINUX=disabled


#tomcat download

wget hdrawhan://apache.tt.co.kr/tomcat/tomcat-8/v8.0.36/bin/apache-tomcat-8.0.36.zip

unzip apache-tomcat-8.0.36.zip

mv apache-tomcat-8.0.36 tomcat8

sudo mv tomcat8 /usr/share

sudo chown -R drawhan.drawhan /usr/share/tomcat8


vi /usr/share/tomcat8/conf/context.xml


<!-- 주석처리 -->

<!-- <Valve className="org.apache.catalina.valves.CometConnectionManagerValve" /> -->

<!-- 추가 -->

<Valve className="com.r.tomcat.session.management.RequestSessionHandlerValve" />

<Manager className="com.r.tomcat.session.management.RequestSessionManager" />


#redis config 추가

vi /usr/share/tomcat8/conf/RedisDataCache.properties


# redis hosts ex: 127.0.0.1:6379, 127.0.0.2:6379, 127.0.0.2:6380, ....

redis.hosts=drawhanredis1:7000, drawhanredis2:7001, drawhanredis3:7000

# Redis Password

redis.password=

# set true to enable redis cluster mode

redis.cluster.enabled=true


#lib 추가 ( hdrawhans://github.com/ran-jit/TomcatClusterRedisSessionManager )

cp TomcatClusterEnabledRedisSessionManager-1.0.jar  /usr/share/tomcat8/lib



PippoPorxy

Boost Tomcat performance for static content
PippoProxy offers an alternative to standard Apache-Tomcat implementations
By Gino Tesei, JavaWorld.com, 02/28/05

원문 : http://www.javaworld.com/javaworld/jw-02-2005/jw-0228-pippo.html?page=1



더 성능이 좋은 이유는 자체 캐싱 알고리즘이 더 훌륭해서라는 군요.

LRU(least-recently used)를 GoF의 Chain of Responsibility 패턴을 사용해서

구현했다고 하는데, 어쨌든 중요한 것은 이 걸 쓰면 더 빠르다라는 내용입니다.


원문을 보시면 설치하는 방법도 좀 나와있으니, 혹 Proxy서버 성능 개선에

관심있으신 분들께선 한 번 써보시고 쓸만한지 좀 알려주세요.


Sponsored by:



This story appeared on JavaWorld at
http://www.javaworld.com/javaworld/jw-02-2005/jw-0228-pippo.html

Boost Tomcat performance for static content

PippoProxy offers an alternative to standard Apache-Tomcat implementations

Protecting corporate internal networks from hackers, thieves, or high load traffic is a common concern. A typical security measure consists of placing an intermediary Web server, known as an HTTP(S) proxy server, between the Internet and the internal network for controlling access. Such an intermediary Web server forwards HTTP(S) requests from clients to other servers, making those requests look like they originated from the proxy server and vice versa (reverse proxy).

Malicious users or excessive traffic load are just two good reasons for controlling access to internal servers. More generally, they are two application scenarios of the well-known structural design pattern Proxy, or Surrogate, whose intent, according to the Gang of Four, (GoF) is to "provide a surrogate or placeholder for another object to control access to it." Obviously such a surrogate adds a cost in terms of complexity. Complex systems are more difficult to understand and any modification is made harder by added complexity. So, why should we pay such a complexity fee? Typical reasons are:

  • Performance: You will eventually have to provide temporary cached instances of resources to multiple clients that share them (Cache Proxy design pattern). For example, this approach makes sense for static content (images, Javascripts, Cascading Style Sheets, static HTML pages).
  • Security: You will eventually have to control access to resources (Protection Proxy). Typically, the proxy server is placed outside the main corporate firewall in the so-called demilitarized zone, while proxied servers are placed inside the firewall in the so-called militarized zone. Furthermore, you will eventually have to provide clients with different levels of access (Protection Access Proxy). For instance, in banking applications, clients may have different levels of access to financial reports.
  • Scalability: As traffic load increases, you will eventually have to add more application servers to maintain good performance, or you might need some kind of automatic failover in case one of the nodes breaks.
  • Audit: You will eventually have to count the number of accesses or trace access details to requested references (Smart Reference Proxy). For instance, this approach is common in banner advertising applications.



 

When such complexity is necessary in an application, PippoProxy, a Java HTTP proxy designed and implemented for Tomcat, can be used in place of standard Apache-Tomcat solutions. This article presents the rationale behind the development of PippoProxy, the need for this type of proxy, and its advantages over more traditional proxies. In addition, PippoProxy's typical deployment scenarios and comparison benchmarks to more traditional solutions are presented.

Typical Apache-Tomcat proxy configurations

The standard Apache-Tomcat proxy configuration places an Apache (proxy) HTTP server before the Tomcat application servers in a "neutral zone" between the company's private network and the Internet (or some other outside public network) for secure access to the company's private data. This proxy server also acts as a load balancer and as a server of static content. Figure 1 shows such a configuration scenario.

Figure 1. Typical Apache-Tomcat configuration

To connect Apache to Tomcat, you can choose one of the standard connectors. For production deployment, mod_jk is the best choice (see Tomcat FAQ and "Fronting Tomcat" for further details). In particular, the mod_jk connector is said to provide approximately double the performance than mod_proxy for several reasons, including a persistent connection pool to Tomcat and a custom optimized protocol named AJP (see the Apache Jakarta Tomcat Connector). For a step-by-step explanation on how to connect an array of Tomcats to Apache using such a connector, see "High Availability Tomcat" (JavaWorld, December 2004).

Limitations of Apache-Tomcat

In typical Apache-Tomcat configurations, static content lives with the proxy server and is typically served without processing by filters or security constraints (Figure 1). This architecture proves inadequate for those security-conscious environments that deliver documents from internal servers to external customers in a controlled manner according to specific business rules inherently bound to the application itself and its lifecycle.

The following section considers application scenarios where such inadequacies are evident and where the adoption of a Tomcat-embeddable HTTP proxy has clear advantages.

Sample application: Managing financial reports at MegaBank

Consider a banking application at MegaBank, a large financial institution, where customers may have different levels of access to financial reports (PDF files, for instance) or other documents such as research produced by financial advisors regarding the companies they are considering investing in. These documents are typically provided by a content management system (CMS) that deploys them in an internal Web server (not Tomcat, in general), for which our initial Web application acts as a service consumer. For example, a user request to access a particular report is processed according to the user profile and other business rules before the document is delivered from the internal server. Moreover, such a CMS and its Web server typically live in a more internal security layer than our Web application. Figure 2 shows such a scenario.

Figure 2. The MegaBank application scenario

In the standard Apache-Tomcat configuration, the Web server is responsible for proxying the documents after applying business logic. Since the internal Web server will not necessarily be Tomcat, the proxy must use the mod_proxy module. Besides performance penalties related to mod_proxy, this solution has the main disadvantage of not complying with standard security policies since the proxy server must pass through two firewalls, see Figure 3.

Figure 3. Using Apache for proxying, the external Web server should be enabled to pass through two firewalls

To block out malicious requests for internal resources protected by security constraints, all business rules used by Tomcat to filter requests to Apache should be replicated (using another programming language) by Apache itself. Thus, the whole setup is difficult to manage. In addition to the correct forwarding of HTTP headers up and down the chain, Apache must also include additional modules (e.g., mod_rewrite and mod_auth) to implement such rules that must remain consistent with the rest of the system. For further details see "URL Rewriting Guide." In particular, this setup violates two design-level principles:

  • Once and only once: This is a principle within agile development methods, such as extreme programming, that strives to eliminate code and data duplication. Generally, if you find yourself duplicating a code fragment or datastructure, you should instead create abstractions or use indirection to remove the duplication. Applying the same philosophy at the enterprise level, this principle means you should not allow different modules to perform the same logical work.
  • Law of Demeter: The simple version of this guideline is "only talk to your immediate friends." Bringing this philosophy from the object-oriented design level to the enterprise level, applications should talk only to those applications on the functional levels immediately above or below them.



 

Other scenarios

The above limitations of typical Apache-Tomcat configurations are related to a quite complex application scenario. Simple scenarios, on the other hand, might also suffer from lack of resources when, for instance, for simple or internal Websites, no Apache Web server is available for proxy use. In addition, in the case of static content (or quasi-static content, as in the MegaBank example), caching mechanisms are important for boosting performance and reducing the offered load to internal Web servers not built for production use.

To summarize, application scenarios are common where the adoption of a Tomcat-embeddable HTTP proxy has clear advantages. Enter PippoProxy.

PippoProxy

PippoProxy is a 100 percent pure Java HTTP proxy designed/implemented for Tomcat that can be used instead of standard Apache-Tomcat solutions. Technically, it is implemented as a servlet and requires:

  • J2SE 1.4.1 or newer
  • Apache Ant 1.6.2 or newer
  • Apache Tomcat 5.0.x or newer



 

Overview

PippoProxy is deployable in one of two modes: it can be plugged into any existent Web application acting as a service provider or serve as a standalone Web application.

In the first deployment scenario, classes responsible for handling business logic may use PippoProxy on demand. For instance, in our MegaBank example, a user request to access a particular report may be processed according to a user profile and other business rules, and eventually forward to PippoProxy.

In particular, let's assume a front end that uses some kind of Model-View-Controller (MVC) framework, whose servlet acts as controller running under http://[domain]:[port]/[context]/servlet/*.[extension]. Such a servlet (or the classes handling its actions in MVC frameworks such as Struts) receives requests from clients, decides whether they have the required authorization, sets a suitable request/session attribute to some value, and forwards the request to PippoProxy, running, for example, under http://[domain]:[port]/[context]/proxy/.

PippoProxy checks the attribute, fetches the required resource from the internal server (or its cache, if it is static), and returns the resource to the client (see Figure 4). This way, malicious users attempting to directly request resources under security constraints without the required authorization fail since their HTTP request/session has no authorization attribute set.

Figure 4. PippoProxy applied to MegaBank example

To maximize performance, PippoProxy manages a persistent (configurable) connection pool to the internal server, avoiding the opening and closing of connections for each request. In the case of static content, the performance is further improved with an efficient caching mechanism that uses a hierarchical structure both in memory and in the filesystem. Such a caching structure consists of a chain composed of a first node for memory cache, followed by another node for the filesystem cache. PippoProxy's caching mechanism implements the well-known GoF behavioral design pattern Chain of Responsibility, see Figure 5.

PippoProxy's main servlet asks for a resource to the first node. If the node has the requested resource, it returns it; otherwise, the node passes the request along the chain to the second node. If the second node has the requested resource, it returns it; otherwise the second node fetches the resource from the internal Web server.

Figure 5. PippoProxy caching structure

The memory and filesystem cache can be configured to have a maximum size (in MB). The cache use a LRU (least-recently used) replacement strategy to decide which resource to force-pass to successive nodes or remove (last node only). Also, the cache is structured as an exclusive cache hierarchy, meaning that the contents of the memory and filesystem nodes are exclusive (eliminating redundant copies). See PippoProxy documentation for further details about implementation. Also see the discussion on Ephemeral Cache Item in Java Enterprise Design Patterns for a sample of a LRU cache in Java.

The following sections show how to install, configure, and deploy PippoProxy.

For the impatient

If you don't already have Tomcat or Ant, download the recent copies and install them. Then download PippoProxy and unpack it in a directory (e.g., /usr/local/pippoproxy). Edit the _ant.properties file, have deploy_local point to the local Tomcat Web applications (e.g., /usr/local/tomcat/webapps), and set application_name to the name of the Web context under which PippoProxy works (e.g., pp). Now the command line ant deploy deploys PippoProxy under the local Tomcat, producing the output shown in Figure 6.

Figure 6. PippoProxy deployment for the impatient

To test your installation, go to http://localhost:8080/pp/lp/ and you should see a well-known Website.

Custom configuration

PippoProxy can also be deployed as a standard J2EE application. As a result, the related web.xml deployment descriptor must contain a servlet element for specifying the servlet name and setting other servlet-specific properties, for example:

  PippoProxyServlet org.pippo.proxy.WebCachedProxyServlet  ENABLE_SESSION_ATTR_KEY_FOR_LOGIN true  ... ... ... 1 



 

The deployment descriptor also must have a servlet-mapping element for mapping it to one or more URL patterns according to the Servlet specification:

  PippoProxyServlet /lp/* 



 

You can complete this customization by either editing your web.xml deployment descriptor or using PippoProxy's Ant scripts. PippoProxy's Ant scripts have all you need if you want to deploy PippoProxy as a standalone Web application. Thus, you should need to edit your web.xml only when you integrate PippoProxy into an existing Web application.

To edit your deployment descriptor, place PippoProxy's Java classes into your J2EE application's WEB-INF/lib directory as a JAR according to the Servlet specification and then edit the web.xml deployment descriptor. For packaging PippoProxy's classes into a JAR, you can use the jarPkg Ant target after editing the _ant.properties file:

 # # Application name. # application_name=pp

# # Local temp directory to do stuff. # outdir=build



 

For example, if your PippoProxy home is /usr/local/pippoproxy, launching ant jarPkg in the previous instance of _ant.properties will package PippoProxy's Java classes into the JAR /usr/local/pippoproxy/build/dist/pp.jar and the HTTP client library used by PippoProxy into the JAR /usr/local/pippoproxy/build/dist/HTTPClient.jar. These JARs should be copied into your J2EE application's WEB-INF/lib directory. Next, edit the web.xml deployment descriptor, setting all parameters, whose semantics are described in the next section.

If you want to deploy PippoProxy as a standalone Web application—running under http://[domain]:[port]/pippo/, for example—edit the _ant.properties file (as above), setting the property deploy_local as below:

 # # Local Tomcat webapps. # deploy_local=C:/java/tomcat/5.0/Tomcat 5.0/webapps



 

So configure the _proxy.properties file (see next section) and launch ant deploy to generate PippoProxy's war file and copy it into Tomcat Web applications automatically.

As a further deployment mode, you can allocate a complete Tomcat server as a proxy server using PippoProxy. In this mode, PippoProxy will proxy all URLs matching the pattern http://[domain]:[port]/* (for further details, see PippoProxy documentation).

Advanced settings

This section describes parameters used by PippoProxy, whether you configure them manually in your J2EE application's deployment descriptor or edit them in the _proxy.properties file.

  • ENABLE_SESSION_ATTR_KEY_FOR_LOGIN: True for checking the value of a session attribute before proxying (e.g., true).
  • SESSION_ATTR_KEY_FOR_LOGIN: The eventual attribute to check before proxying (e.g., my_attr_for_proxy).
  • CACHE_ENABLED: True (static content only) for enabling PippoProxy's cache (e.g., true).
  • CACHE_TIMEOUT: Lifetime (in milliseconds) of a resource before being removed from the cache (e.g., 3600000).
  • CACHE_MAX_MEMORY_SIZE: The maximum size (in MB) of memory cache (e.g., 10).
  • CACHE_MAX_DISK_SIZE: The maximum size (in MB) of filesystem cache (e.g., 50).
  • CACHE_PATH_DIR: The absolute path where resources are stored (e.g., /usr/local/pippoproxy/cache).
  • REMOTE_HOST: The remote host to proxy (e.g., cocoon.apache.org).
  • REMOTE_PORT: The remote port to proxy (e.g., 80).
  • IS_ROOT: True for making the application server act as a proxy server (e.g., true); in this mode, PippoProxy handles all http://[domain]:[port]/* URLs. See PippoProxy documentation for further details.
  • LOCAL_PREFIX: The local prefix under which PippoProxy runs (e.g., by setting /lp, PippoProxy handles all client requests matching the URL pattern http://[domain]:[port]/[context]/lp/*).
  • REMOTE_PREFIX: The remote prefix to proxy (e.g., by setting /2.1, a client request for http://[domain]:[port]/[context]/lp/index.html will map to http://[REMOTE_HOST]:[REMOTE_PORT]/2.1/index.html).
  • NOT_ALLOWED_HEADERS: The pipe-separated list of HTTP headers to block (e.g., Content-Encoding|Content-Type).
  • PROXY_ENABLED: True if a further proxy server is available for reaching the internal server to proxy (e.g., true).
  • PROXY_HOST: The eventual proxy host for reaching the internal server to proxy (e.g., my_company.proxy).
  • PROXY_PORT: The eventual proxy port for reaching the internal server to proxy (e.g., 80).
  • PROTOCOL: Only http currently supported.
  • INIT_CONNECTION: The initial number of HTTP connections (e.g., 10).
  • MAX_CONNECTION: The maximum HTTP connections in the pool (e.g, 15).



 

Performance comparisons

This section compares the performance of PippoProxy deployed to Tomcat (5.5.4) and a standard Apache(2.0.49)-Tomcat(5.5.4) pair connected with mod_jk2 in the same machine.

Note: mod_jk2 has been configured so that the required resources are not forwarded by Apache to Tomcat, but handled locally by Apache itself (workers2.properties file).

Testing platform:

  • Intel 730 GHz Pentium III
  • Single processor
  • 256 MB RAM
  • Linux 2.6.8



 

The tests attempt to simulate user scenarios where the same set of static resources is requested at a high repetition rate, hence, where the efficiency of the cache directly affects overall performance. Two file sizes have been used, 13 KB and 128 KB, and the same resource is requested 1,000 and 500 times, respectively.

As shown in Figure 7, the performance gap relative to the standard proxy increases as the file size increases. This is due to the fact that the PippoProxy cache works on the well-known principle of temporal locality, where programs tend to reuse data and instructions they have recently used (see Resources for more information).

Figure 7. Performance of PippoProxy compared with a traditional Apache-Tomcat pair for file sizes of 13 KB and 128 KB. Click on thumbnail to view full-sized image.

For small files, mod_jk and PippoProxy performances are quite similar, since the file initial-access time is a substantial portion of the total time for handling requests. But for larger files, differences among diverse caching behaviors are more evident. Though elapsed time for first calls is quite comparable, after 500 calls, on average, PippoProxy is five times faster than mod_jk. See Figure 8, where PippoProxy's throughput increases as the file size increases, while mod_jk throughput decreases.

Figure 8. PippoProxy's throughput increases as the file size increases. Click on thumbnail to view full-sized image.

This comparison shows how PippoProxy's built-in cache speeds up the delivery of static content for medium to large file sizes, without any penalty on the delivery of dynamic content, since the latter is managed in both cases by the supporting servlet engine.

Conclusion

This article introduced PippoProxy, a one-to-one replacement for standard Apache-Tomcat proxy solutions, and illustrated application scenarios in which this Tomcat-embeddable HTTP proxy offers clear advantages over standard Apache-Tomcat proxy solutions. Regardless of the deployment scenario, in case of static (or quasi-static) content, PippoProxy caching proves to be efficient; its performance is five times that of the traditional mod_jk-based proxy. Finally, PippoProxy does not require any optional modules or even a Web server for connecting to Tomcat, and configuration and deployment are easy when using Apache Ant.

I'd like to thank my manager Marco Fillo at Virgilio for his support while preparing this article and Vanessa di Lima for her invaluable assistance.

Author Bio

Gino Tesei is a senior software architect at Virgilio, one of the largest Italian Web portals. He specializes in search engines, e-shopping, and financial Websites. He has previously worked on a variety of projects in the finance and fashion industries. He is the creator of PippoProxy, has authored several technical articles, and is a member of the Java User Group Milano.

All contents copyright 1995-2007 Java World, Inc. http://www.javaworld.com

Tomcat 5에서 클러스터링과 로드밸렁싱-실전적용

실 전산에서 적용한 방법으로 Fail-Over도 지원이 된다.

가중치 라운드 로빈방식으로 할당비중치를 줄수 있으나 다만 문제는 톰켓이 완전히 죽지 않은 상태... 즉 웹 어플리케이션이 GC나 스타팅이 되지 않은 상태에도 할당이 된다....


PS)당연하겠지만.. 톰켓자체가 죽지 않는 상태이니 그럴꺼란 생각이 든다

 

Apache2.0 + Tomcat 5.5.27 + jk-connect

 

1.workers.properties
# workers.properties
worker.list=tomcatlb,tomcat6,tomcat7

worker.tomcatlb.type=lb
worker.tomcatlb.balanced_workers=tomcat6,tomcat7
worker.tomcatlb.sticky_session=1
worker.tomcatlb.local_worker_only=1
 
worker.tomcat6.port=1901
worker.tomcat6.host=localhost
worker.tomcat6.type=ajp13
worker.tomcat6.lbfactor=1
worker.tomcat6.local_worker=1
worker.tomcat6.socket_timeout=60
worker.tomcat6.socket_keepalive=true
 
#worker.tomcat6.cachesize=10
# END workers.properties
 
#tomcat7.tomcat-test.com
worker.tomcat7.port=1902
worker.tomcat7.host=localhost
worker.tomcat7.type=ajp13
worker.tomcat7.lbfactor=1
worker.tomcat7.local_worker=1
worker.tomcat7.socket_timeout=60
worker.tomcat7.socket_keepalive=true

 

2.http.conf


    ServerAdmin root@tomcat-test.com
    DocumentRoot $APACHE2/htdocs
    #ErrorDocument 404 /jsp/base/404.jsp
    ServerName tomcat6.tomcat-test.com
    ErrorLog "|/usr/local/sbin/cronolog $APACHE2/logs/%Y%m/tomcat6.tomcat-test.com-error_log-%Y%m%d"
    CustomLog "|/usr/local/sbin/cronolog $APACHE2/logs/%Y%m/tomcat6.tomcat-test.com-access_log-%Y%m%d" common
 
    JkMount /jsp/* tomcatlb
    JkMount /servlet/* tomcatlb
    JkMount /admin/* tomcatlb
    JkMount /manager/* tomcatlb
    JkMount /probe/* tomcat6
    JkMount /probe2/* tomcat7
    #JkMount /otherworker/*.jsp remoteworker



 

클러스터링 스케줄링


1)라운드-로빈(round-robin)
라운드-로빈 방식은 로드밸런서에 들어오는 요청 패킷들을 차례대로 실제 서버에 할당하는 방식이 다. 이 방식에서 실제 서버의 현재 부하 상황 등은 고려되지 않는다. 단지 차례대로 할당할 뿐이다. 하지만, 이렇게 하더라도 로드밸런싱을 위해 이전에 사용되던 라운드-로빈 DNS 방식에 의해 서버를 할당하는 방식에 비해서는 우수한데, DNS의 경우는 한번 서버가 지정되면 해당 서버에 수많은 요청 패킷이 몰릴 수 있기 때문이다.

2)가중 라운드-로빈(weighted round-robin)
가중 라운드-로빈 방식은 기본적으로 라운드-로빈 방식인데, 각 서버에 서로 다른 가중치를 주어서 할당하는 방식이다. 이 방식을 사용해야 하는 경우는 실제 서버들이 CPU의 수와 성능, 메모리 용량 등 서로 다른 성능을 가지고 있어서, 각 서버를 동등하게 취급할 수 없는 경우이다.

3)최소 연결(least connection)
최소 연결 방식은 실제 서버들 중에서 현재 가장 적은 수의 요청을 처리하고 있는 서버를 선택하여 요청 패킷을 할당하는 방식이다. 이 방식은 실제 서버의 현재 부하 상황을 동적으로 판단하여 요청을 처리하기 때문에, 앞의 두 방식에 비해서 동적으로 우수한 부하 분산 효과를 얻을 수 있다.

4)가중 최소 연결(weighted least connection)
가중 최소 연결 방식은 기본적으로 최소 연결 방식인데, 가중 라운드-로빈 방식과 마찬가지로 각 서버에 서로 다른 가중치를 주어서 할당하는 방식이다.

Tomcat 5에서의 클러스터링과 로드 밸런싱, Part 2

Clustering and Load Balancing in Tomcat 5, Part 2Tomcat 5에서의 클러스터링과 로드 밸런싱, Part 2

by Srini Penchikala
04/14/2004
번역 허태명
이 글은 톰캣 5 서버에서 클러스터링과 로드 밸런싱에 관한 시리즈의 두번째 파트이다. 파트 1에서 필자는 확장성과 고가용성을 위한 시스템을 디자인할 때 고려해야할 다양한 요소와 Large-scale J2EE 시스템 디자인의 개괄에 대해 얘기했다. 또한 클러스터링, 로드 밸런싱, 결함 허용, 세션 복제 능력에 대한 톰캣의 지원에 대해서도 논했다. 이번 파트에서 우리는 제시된 클러스터 설정의 아키텍쳐와 클러스터를 배치할 때(다수의 톰캣 서버 인스턴스를 운영함으로써)의 설치와 설정의 세세한 부분에 대해 다룰 것이다.

제시된 클러스터 설정

아래 목록은 제시된 톰캣 클러스터에서 필자가 달성하기 원하는 주 목적들이다:
  • 클러스터는 고도의 확장성이 있어야 한다.
  • 클러스터는 결함 허용을 지원해야 한다.
  • 클러스터는 동적으로 설정가능해야 하며, 그것은 클러스터를 프로그램적(자바 코드의 변경)으로 보다는 선언적(설정 파일의 변경)으로 관리하기 쉬워야 한다는 것을 의미한다.
  • 클러스터는 자동 클러스터 멤버 감지 기능을 제공해야만 한다.
  • 세션 데이터를 위한 메모리 세션 상태 복제 기능을 갖고 있는 Fail-over와 로드 밸런싱 기능
  • 플러그인/설정가능한 로드 밸런싱 정책
  • 클러스터 멤버가 합류하거나 그룹에서 떠날 때 그룹 멤버쉽 공지 기능
  • 멀티캐스트를 통한 메세지 전송의 손실이 없어야 한다.
  • 클러스터링은 웹 어플리케이션과 서버에 잘 연계되어야 한다. 클러스터는 클라이언트와 서버 양쪽에 투명성을 제공해야 한다. 클라이언트 투명성은 클라이언트가 클러스터링된 서비스나 클러스터가 어떻게 설정됐는지 모른다는 의미이다. 클러스터는 각각의 서비스들 보다는 단일한 것으로써 확인되고 접근되어야 한다. 서버 투명성은 서버의 어플리케이션 코드는 그것이 클러스터 내에 있는지 몰라야 한다는 것을 의미한다. 어플리케이션 코드는 다른 클러스터 멤버와 통신할 수 없다.
클러스터링 환경을 설정하기 위해 4개의 톰캣 서버 인스턴스가 설치되었다. 톰캣은 로드 밸런싱과 클러스터링 양쪽의 요구사항을 위해 사용되었다. 클러스터 설정은 수직 확장 방법 (단일 머신에 다수의 톰캣 서버 인스턴스 운영)을 사용하였다. 1개의 서버 그룹과 2개의 복제본이 클러스터에 설정되었다.(서버 그룹은 어플리케이션 서버의 논리적 표시이다.) 복제본은 세션 복제를 최적화하기 위해 서버 그룹과 설정(웹 어플리케이션의 디렉토리 구조와 컨텐츠를 의미)을 완전히 똑같이 한다. 다음은 제시된 클러스터 설정의 주요 컴포넌트들이다:
  • 로드 밸런서: 1개의 톰캣 인스턴스가 클러스터 노드들 사이에서 트래픽을 분배하기 위해 설정되었다. 이 인스턴스는 TC-LB라는 코드명을 갖는다.
  • 클러스터링: 3개의 톰캣 서버 인스턴스가 클러스터의 일부로서 운영된다. 이 인스턴스들의 코드명은 TC01, TC02, TC03 이다.
  • 세션 영속성: 메모리 세션 복제가 세션 영속성 메카니즘으로 선택되었다. 세션 객체가 변경될 때마다 세션 데이터는 3개의 모든 클러스터 멤버들에게 복사된다.
  • Fail-Over: 톰캣 설치본에 포함되어 있는 밸런서 어플리케이션은 fail-over를 다루도록 설계되지 않았다. 필자는 어떤 리퀘스트라도 서버에 포워딩하기전에 서버의 상태를 체크하는 ServerUtil이라는 유틸리티 클래스를 작성하였다. ServerUtil은 클러스터 노드의 상태를 검증하는 2개의 메소드를 가지고 있다. 첫번째 메소드에서는 특정 서버 인스턴스가 현재 돌아가고 있는지 아닌지 체크하기 위해 McastService를 사용한다. 두번째 메소드는 파라미터로써 전달된 웹 페이지 URL에 의거하여 URL 객체를 생성함으로써 클러스터 노드의 사용가능성을 검증한다. 이 클래스를 사용하기 위해 catalina-cluster.jar (%TOMCAT_HOME%/server/lib 디렉토리에 위치)와 commons-logging-api.jar (%TOMCAT_HOME%/bin 디렉토리에 위치) 파일이 클래스패쓰에 잡혀있는지 확인하라.
Figure 1의 어플리케이션 아키텍쳐 다이어그램은 클러스터의 주요 컴포넌트를 보여준다.
Figure 1
Figure 1. Tomcat cluster architecture diagram

톰캣 인스턴스의 설치 & 설정

Table 1. 톰캣 클러스터링을 설정하는데 사용된 머신의 하드웨어/소프트웨어 스펙
Processor HP Pavilion Pentium III 800 MHz
Memory 512 MB RAM
Hard Disk 40 GB
Operating System Windows 2000 server with Service Pack 4
JDK 버전 1.4.0_02 (주: 톰캣 클러스터링을 사용하기 위해서는 JDK 1.4 이상의 버전이 필요하다.)
Tomcat 버전 5.0.19
사용된 툴 Ant 1.6.1, Log4J, JMeter, JBuilder

클러스터 프레임워크의 주요 요소들

자바 클래스

  • BaseLoadBalancingRule
    이 클래스는 커스텀 Rule 클래스에 일반적인 룰 로직을 캡슐화하기 위해 생성된 추상 클래스이다. 예제 웹 어플리케이션에서 사용된 커스텀 로드 밸런싱 룰은 이 베이스 클래스를 확장한 것이다.
  • RandomRedirectRule
    이 클래스는 랜덤한 방법으로 사용가능한 서버에 웹 리퀘스트를 포워드하는 로직을 정의한다. 난수를 생성하기 위한 시드(seed)로 현재의 시스템 시간을 사용한다.
  • RoundRobinRule
    이 클래스는 라운드 로빈 룰에 의거한 로드 밸런싱 룰을 정의한다. 리퀘스트가 올 때 이 클래스는 리퀘스트를 목록의 다음 멤버에게 포워딩한다. 다음 사용가능한 클러스터 멤버를 추적하고 매 새로운 리퀘스트마다 값을 하나씩 증가시키기 위해 정적 변수를 사용한다.
  • ServerUtil
    이 클래스는 특정 클러스터 노드가 리퀘스트를 받을 수 있도록 사용가능한지 아닌지를 체크하기 위해 만들어진 유틸리티 클래스이다. 이 클래스는 클러스터 멤버가 그룹을 떠났는지 검사하기 위해 McastService (org.apache.catalina.cluster.mcast 패키지) 클래스를 사용한다.
이 자바 클래스들간의 관계가 Figure 2의 클래스 다이어그램에 나타나 있다.
Figure 2
Figure 2. Cluster application class diagram

설정 파일

  • server.xml
    이 파일은 톰캣 서버 인스턴스의 클러스터링을 설정하기 위해 사용된다. 톰캣 설치본에 포함되어 있는 버전은 파일에 자세한 설정이 되어 있지만 주석처리되어 있다.
  • web.xml
    이 설정 파일은 웹 어플리케이션 세션 데이터가 복제될 필요있다는 것을 규정하기 위해 사용된다.
  • rules.xml
    이 파일은 커스텀 로드 밸런싱 룰을 정의하기 위해 사용된다. 이 파일은 우리가 클러스터 멤버 사이의 부하를 분배하기 위해 어떤 로드 밸런싱 룰을 사용하기 원하는지 규정하기 위해 사용하는 파일이다.

스크립트

  • test.jsp
    서버 상태를 체크하기 위해 사용하는 간단한 테스트 JSP 스크립트이다. test.jsp 파일이 실행된 시스템 시간과 톰캣 인스턴스의 이름을 보여준다.
  • testLB.jsp
    이 파일은 우리의 예제 웹 어플리케이션의 시작 페이지이다. HTML 리다이렉트를 사용하여 로드 밸런서 필터로 웹 리퀘스트를 포워딩한다.
  • sessiondata.jsp
    이 스크립트는 어떤 1개의 클러스터 노드가 다운됐을 때 세션 데이터의 손실이 없다는 것을 검증하기 위해 사용된다. 세션의 자세한 정보와 또한 HTTP 세션 객체를 조작하는 HTML 필드를 보여준다.
  • build.xml
    Ant 빌드 스크립트가 톰캣 인스턴스의 시작과 중지 작업을 자동화하기 위해 사용된다.(이 스크립트를 실행하기 위해 Ant 1.6.1 최신 버전이 사용됐다.) 톰캣 인스턴스가 성공적으로 구동되면, IP 주소와 포트 번호를 정함으로써 어떤 톰캣 인스턴스가 실행되는지 검증하기 위해 test.jsp를 호출할 수 있다. JSP 페이지는 현재 시스템 시간과 톰캣 인스턴스의 이름을 보여준다.(여러분은 자신의 환경에서 스크립트를 실행하기 위해 build.properties 파일에 정의된 톰캣 서버의 홈 디렉토리를 변경할 필요가 있다.)
톰캣 인스턴스를 시작하거나 중지시키는 빌드 스크립트의 몇 가지 타겟들:
  • 특정 톰캣 인스턴스를 구동하기 위해 start.tomcat5x 타겟을 호출하라. (예: tomcat50)
  • 특정 톰캣 인스턴스를 중지하기 위해 stop.tomcat5x 타겟을 호출하라.
  • 실행되고 있는 모든 톰캣 인스턴스를 중지하기 위해 stop.alltomcats 타겟을 호출하라.

예제 코드

이 글에서 사용된 예제 코드는 tomcatclustering.zip에서 구할 수 있다. 톰캣 서버 인스턴스들을 설치한 후에(4개가 필요하다.), 톰캣 디렉토리에 zip 파일의 압축을 풀어라. 제공된 예제 코드는 로드 밸런싱 정책으로 RoundRobinRule 방식을 사용한다. 만약 랜덤 리다이렉트 정책을 사용하고 싶다면, "tomcat50/webapps/balancer/WEB-INF/config" 디렉토리에 위치한 rules.xml 파일을 변경하라. RoundRobinRule 룰 엘리먼트를 주석처리하고, RandomRedirectRule 룰 엘리먼트의 주석을 제거하라. 또한 클러스터의 톰캣 인스턴스를 3개가 아닌 2개로 사용하고 싶다면, 세번째 룰을 주석처리하고 maxServerInstances의 값을 3에서 2로 변경해라.
주 : 필자는 밸런서와 예제 웹 어플리케이션만 남기고 톰캣 설치본에 포함된 다른 모든 웹 어플리케이션(jsp-examples, 등등)은 제거했다.

HTTP Request 흐름

예제 클러스터 환경에서의 웹 리퀘스트의 흐름은 다음과 같다:
  1. 시작 페이지 호출 (http://localhost:8080/balancer/testLB.jsp).
  2. JSP는 밸런서 필터로 리퀘스트를 리다이렉트 시킨다. (URL: http://localhost:8080/balancer/LoadBalancer).
  3. 로드 밸런서(TC-LB)로서 실행되는 톰캣 서버는 웹 리퀘스트를 인터셉트하고 설정 파일에 정의된 로드 밸런싱 알고리즘에 의거하여 다음 사용 가능한 클러스터 멤버(TC01, TC02, TC03)로 리다이렉트 시킨다.
  4. 선택된 클러스터 멤버의 JSP 스크립트 sessiondata.jsp("clusterapp" 웹 어플리케이션에 위치한)가 호출된다.
  5. 만약 세션이 변경되었다면 ClusterAppSessionListener에 있는 세션 리스너 메소드가 세션 변경 이벤트를 기록하기 위해 호출된다.
  6. sessiondata.jsp는 웹 브라우저에 세션의 자세한 데이터를 보여준다.(세션 ID, 마지막 접근 시간, 등등)
  7. 랜덤하게 하나 또는 2개의 클러스터 노드를 중지시킨다.(Ant 스크립트의 "stop.tomcat5x" 타겟을 호출함으로써)
  8. 사용가능한 클러스터 멤버중의 하나로 리퀘스트 fail over가 발생하는지 살펴보기 위해 위의 단계 1부터 7까지 반복한다. 또한 데이터의 손실없이 클러스터 멤버로 세션 정보가 복사되었는지 체크해 본다.
Figure 3은 웹 리퀘스트 흐름을 시퀀스 다이어그램으로 보여준다.
Click for larger view
Figure 3. Cluster application sequence diagram (큰 화면으로 보기 위해 그림을 클릭하시오)

클러스터 설정

"clusterapp"라고 불리는 예제 웹 어플리케이션은 클러스터 내에서 실행하기 위해 만들어졌다. 세션 복제를 최적화하기 위해 모든 인스턴스는 같은 디렉토리 구조와 컨텐츠를 가진다.
클러스터 내의 톰캣 서버 인스턴스는 세션을 전송하기 위해 IP 멀티캐스트를 사용하기 때문에, 우리는 IP 멀티캐스트가 클러스터가 설정된 머신에 사용가능한지 확인할 필요가 있다. 이것을 검증하기 위해, Tomcat: The Definitive Guide 책에서 제공하는 MulticastNode라는 예제 자바 프로그램을 실행하거나 또는 멀티캐스트 서버와 클라이언트 프로그램을 작성하는 법에 관해 자바 소프트 웹 사이트에서 구할 수 있는 예제 튜토리얼을 참조하라.
클러스터 노드가 시작될 때, 클러스터 내의 다른 멤버들은 새로운 멤버가 클러스터에 추가됐다는 로그 메세지를 서버 콘솔에 보여준다. 비슷하게, 클러스터 노드가 다운될 때, 나머지 멤버들은 클러스터에서 멤버가 제거됐다는 로그 메세지를 콘솔에 보여준다. Figure 4는 클러스터 노드가 클러스터에서 제거되거나 또는 새로운 멤버가 클러스터에 추가됐을 때, 톰캣 콘솔의 로그 메세지를 보여준다.
Figure 4
Figure 4. 클러스터에서 멤버가 추가되거나 제거됐을 때의 로그 메세지
톰캣 서버에서 클러스터링과 세션 복제를 사용하기 위해 아래의 절차를 따라라:
  1. 세션에 저장할 모든 객체는 java.io.Serializable 인터페이스를 구현해야 한다.
  2. server.xml 파일의 Cluster 요소의 주석을 제거하라. Cluster 요소의 useDirtyFlagreplicationMode는 빈도수와 세션 복제 메카니즘의 최적화를 위해 사용된다.
  3. server.xml 파일의 Valve 요소의 주석을 제거함으로써 ReplicationValve를 활성화시켜라. 만약 세션이 웹 클라이언트에 의해 변경된다면 ReplicationValve는 HTTP 리퀘스트를 인터셉트하고 클러스터 멤버들 사이의 세션 데이터를 복제하기 위해 사용된다. Valve 요소는 세션을 변경할 수 없는 리퀘스트(HTML 페이지나 이미지 파일과 같은)를 걸러내기 위해 사용할 수 있는 "filter" 속성을 가지고 있다.
  4. 단일 머신에서 3개의 톰캣 인스턴스가 실행되고 있기 때문에, tcpListenPort 속성은 각 톰캣 인스턴스에 대해 유니크하게 설정된다. mcastXXX로 시작하는 속성들 (mcastAddr, mcastPort, mcastFrequency, mcastDropTime)은 클러스터 멤버쉽 IP 멀티캐스트 핑을 위한 것이고, tcpXXX로 시작하는 속성들(tcpThreadCount, tcpListenAddress, tcpListenPort, tcpSelectorTimeout)은 TCP 세션 복제를 위한 것이라는 것을 아는 것은 중요하다. (아래의 "클러스터링 설정 파라미터" 테이블은 클러스터링을 사용하기 위해 톰캣 서버 인스턴스들의 설정이 어떻게 다른지 보여준다.)
  5. web.xml 메타 파일(clusterappWEB-INF 디렉토리에 위치)은 <distributable/> 요소를 가져야 한다. 특정 웹 어플리케이션에 대해 세션 상태 복제를 하기 위해, distributable 요소는 어플리케이션을 위해 정의될 필요가 있다. 이것은 세션 복제를 필요로 하는 웹 어플리케이션이 하나 이상이라면, 모든 웹 어플리케이션의 web.xml 파일에 distributable 를 추가할 필요가 있다는 것을 의미한다. Tomcat: The Definitive Guide 책의 톰캣 클러스터링 챕터는 이 주제에 대해 매우 훌륭하게 설명하고 있다.
Table 2. 클러스터링 설정 파라미터
Configuration Parameter Instance 1 Instance 2 Instance 3 Instance 4
Instance Type Load Balancer Cluster Node 1 Cluster Node 2 Cluster Node 3
Code name TC-LB TC01 TC02 TC03
Home Directory c:/web/tomcat50 c:/web/tomcat51 c:/web/tomcat52 c:/web/tomcat53
Server Port 8005 9005 10005 11005
Connector 8080 9080 10080 11080
Coyote/JK2 AJP Connector 8009 9009 10009 11009
Cluster mcastAddr 228.0.0.4 228.0.0.4 228.0.0.4 228.0.0.4
Cluster mcastPort 45564 45564 45564 45564
tcpListenAddress 127.0.0.1 127.0.0.1 127.0.0.1 127.0.0.1
Cluster tcpListenPort 4000 4001 4002 4003
주: 모든 클러스터 멤버는 동일한 물리적 머신에서 실행되고 있기 때문에, 같은 IP 주소를 사용한다 (127.0.0.1).
톰캣 인스턴스를 시작하고 중지하기 위해 Ant 스크립트를 사용하지 않는다면, 테스트 머신에 CATALINA_HOME 환경 변수를 설정하지 말아라. 이 변수가 설정되면, 모든 인스턴스는 톰캣 인스턴스를 구동하기 위해 같은 디렉토리(CATALINA_HOME 변수에 정의된)를 사용하려고 할 것이다. 그러한 결과로, 오직 최초의 인스턴스만 성공적으로 구동되고 나머지 인스턴스들은 포트가 이미 사용되고 있다는 바인드 익셉션 메세지와 함께 구동에 실패할 것이다: "java.net.BindException: Address already in use: JVM_Bind:8080".

로드 밸런서 설정

필자는 웹 리퀘스트를 리다이렉트 시키기 위해 룰 API를 상속받은 두개의 간단한 커스텀 로드 밸런싱 룰을 작성했다.(RoundRobinRuleRandomRedirect) 이 룰들은 라운드 로빈과 랜덤 리다이렉트와 같은 로드 밸런싱 알고리즘에 의거한다. 여러분들도 가중치 기반, 마지막 접속 시간 등과 같은 다른 요소들을 기반으로 하는 비슷한 커스텀 로드 밸런싱 룰을 작성할 수 있다. 톰캣 로드 밸런서는 파라미터 기반의 로드 밸런싱 룰을 예제로 제공한다. 이것은 HTTP 리퀘스트의 파라미터에 따라 웹 리퀘스트를 다른 URL로 리다이렉트 시킨다.
TC-LB 인스턴스는 클러스터 멤버로 사용하기 않기 때문에, TC-LB의 server.xml의 cluster와 valve 요소는 주석을 제거하지 말아라.

테스팅 설정

세선 영속성 테스팅

세션 영속성 테스팅에서 주 목적은 클러스터 멤버가 웹 리퀘스트 처리 도중 다운됐을 때 세션 데이터가 손실되지 않았다는 것을 검증하는 것이다. sessiondata.jsp 페이지는 세션의 자세한 정보를 보여주기 위해 사용됐다. 이 스크립트는 또한 세션 속성을 추가/변경/제거 하기 위한 HTML 텍스트 필드를 제공한다. HTTP 세션에 몇 가지 속성을 추가한 후에, 필자는 랜덤하게 클러스터 노드를 중지시키고 나머지 사용가능한 클러스터 멤버에서 세션 데이터를 체크했다.

부하 테스팅

부하 테스팅의 목적은 커스텀 로드 밸런싱 알고리즘을 연구하고, 특히 하나 또는 그 이상의 노드가 다운됐을 때 웹 리퀘스트가 얼마나 효율적으로 클러스터의 노드에 분배되는지 알아보는 것이다. JMeter 부하 테스팅 툴이 다수의 동시 접속 웹 유저를 시뮬레이트 하기 위해 사용됐다.
클러스터 설정에서 로드 밸런싱을 테스트하기 위한 절차:
  1. 로드 밸런서와 톰캣 서버의 클러스터 인스턴스들을 구동한다.
  2. 시작 JSP 스크립트를 구동한다. (testLB.jsp).
  3. 랜덤한 간격으로 하나 또는 그 이상의 서버를 중지시켜서 서버 다운을 시뮬레이트한다.
  4. 부하 분배 패턴을 체크한다.
  5. 절차 1부터 4를 100번 반복한다
모든 로그 메세지는 tomcat_cluster.log (tomcat50/webapps/balancer 디렉토리에 위치) 라고 불리는 텍스트 파일에 기록된다. 시퀀스 다이어그램(Figure 2)에서 보여준 모든 웹 객체에 대한 응답 시간이 Log4J 메세지를 사용하여 기록되었다. 경과시간(밀리세컨드) 자료들이 수집되고 테이블 3에 자료로 나타나 있다. 필자는 테스트동안 응답 시간을 수집하는데 Designing Performance Testing Metrics into Distributed J2EE Apps에 기술된 것과 비슷한 방법론을 따랐다.
다음 테이블은 각각 부하 테스팅 (RoundRobinRule을 사용)의 경과시간과 부하 분배율 (RandomRedirectRule을 사용) 보여준다.
Table 3. 부하 테스팅 경과시간
# Scenario testLB.jsp
(ms)
RoundRobinRule
(ms)
sessiondata.jsp
(ms)
Total
(ms)
1 3개의 모든 서버 인스턴스가 실행중 54 76 12 142
2 2개의 서버 인스턴스가 실행중 (TC02은 중지상태) 55 531 14 600
3 1개의 서버 인스턴스만 실행중
(TC01, TC02 중지상태)
56 1900 11 1967
주: 모든 경과시간은 100명의 동시접속 유저의 부하를 바탕으로한 평균값이다.
Table 4. 랜덤 LB 정책을 사용했을 때 부하 분배
# Scenario TC01 (%) TC02 (%) TC03 (%)
1 3개의 모든 서버 인스턴스가 실행중 30 46 24
2 2개의 서버 인스턴스가 실행중 (TC02은 중지상태) 56 0 44
주: 부하 분배율은 100명의 동시접속 유저의 부하를 바탕으로한 값이다.

결론

세션 영속성 테스팅에서, 세션 속성을 추가한 후에 클러스터 노드 중의 하나가 다운되고 세션 속성은 서버 다운때문에 손실되지 않았음을 검증했다. 텍스트 파일에 기록된 세션의 자세한 정보는 세션 속성의 상세값을 연구하기 위해 사용됐다.
부하 테스팅에서 하나 또는 2개의 서버 인스턴스가 중지되고 오직 하나의 톰캣 인스턴스만 실행중일 때, 응답 시간은 3개의 인스턴스가 모두 사용가능 했을 때에 비해 길어졌다. 중지된 인스턴스가 재시작됐을 때, 로드 밸런서는 자동적으로 서버가 다시 리퀘스트를 처리할 수 있도록 사용가능하다는 것을 알아냈고 다음 웹 리퀘스트를 리다이렉트 시켰다. 이것은 응답 시간을 현저하게 향상시켰다.
클러스터 멤버가 사용가능한지 알아보기 위해(ServerUtil 클래스를 사용하여) 필자가 사용한 메카니즘은 가장 빠른 방법은 아니다. 더욱 복잡하고 견고한 fail-over 테크닉들이 실세계의 시나리오에서 사용되어야 할 것이다.
제시된 클러스터 설정의 한계점 중의 하나는 단지 하나의 로드 밸런서만 제공한다는 것이다. 로드 밸런서 역할을 하는 톰캣 인스턴스가 다운된다면 어떤 일이 벌어지나? 어떤 클러스터 멤버에게도 리퀘스트를 포워딩할 방법이 없고, 그러한 결과로 이것을 소위 Single Point of Failure(SPoF)라고 한다. 이 문제에 대한 한가지 해결책은 만약 주 로드 밸런서가 다운된다면 그 역할을 이어받을 대기 로드 밸런서로서 두번째 톰캣 인스턴스를 가지고 있는 것이다. 전형적인 HA(High Availability) 옵션은 SPoF 상황을 방지하기 위해 2개의 로드 밸런서를 가지고 있는 것을 포함한다.
예제 클러스터 설정에서, 모든 톰캣 인스턴스(로드 밸런서를 포함해서)는 단일 컴퓨터에서 실행되도록 설정되었다. 더 좋은 디자인은 로드 밸런서를 클러스터 멤버로부터 분리된 머신에서 실행하는 것이다. 또한 수평 확장 방법론의 이점과 클러스터 성능을 향상시키기 위해 클러스터 노드를 1 머신당 2개로 제한하는 것이다.
HTTP 세션 복제는 J2EE 웹 어플리케이션 서버에서 값비싼 작업이다. 클러스터링된 J2EE 환경에서 세션 관리의 포함은 웹 어플리케이션이 제품 환경에 구현될 때까지 기다리는 것보다 프로젝트의 분석과 디자인 단계에서 고려되어야 한다. 어플리케이션 코드는 클러스터 환경을 염두에 두고 디자인되어야만 한다. 만약 클러스터링 환경을 포함하는 것이 디자인 단계에서 고려되지 않는다면, 클러스터 설정에서 작동하기 위해 코드는 아마도 완전히 재작성될 필요가 있을 수도 있고, 이것은 매우 값비싼 노력이 될 것이다.
만약 웹 어플리케이션이 객체 캐슁 메카니즘에 대한 어떤 종류의 지원이라도 한다면, 클러스터 환경에서의 객체의 캐슁은 어플리케이션 개발의 초기단계부터 고려되어야만 한다. 캐쉬된 데이터를 모든 클러스터 노드에서 동기화된 상태로 가지는 것은 웹 유저에게 정확하고 최신으로 갱신된 비지니스 데이터를 제공하기 위해 핵심적이기 때문에 이것은 매우 중요하다. 또 다른 중요한 고려사항은 클러스터 내의 만료된 세션 데이터를 제거하는 것이다.
J2EE 클러스터가 성공적으로 설정되고 실행되면, 확장성과 고가용성의 이점을 제공하기 위해 그 관리와 유지보수가 매우 중요해질 것이다. 클러스터에 많은 노드를 가지고 있으면, 유지보수는 클러스터가 실행되도록 유지하고 어플리케이션의 변경을 모든 클러스터 노드에 적용하도록 순환해야 한다. 이러한 서비스를 제공하는 한가지 방법은 정기적으로 서버의 사용가능성을 체크하고 클러스터의 노드 중 어떤 것이 다운되면 공지하는 모니터링 서비스를 구현하는 것이다. 이 서비스는 다운된 노드를 감지하고 그것을 액티브 노드 목록에서 제거해서 리퀘스트가 다운된 노드로 가지 않도록 정기적인 간격으로 노드를 체크해야 한다. 이 서비스는 변경이 일어날 때마다 클러스터 내의 모든 서버를 업데이트하고 동기화하는 능력을 포함해야 한다. 웹 어플리케이션에 대한 모든 리퀘스트는 로드 밸런싱 시스템을 통해야 하기 때문에, 시스템은 액티브 세션의 수, 어떤 인스턴스에 연결된 액티브 세션의 수, 응답 시간, 피크 부하 시간, 피크 부하 동안의 세션의 수, 최저 부하 동안의 세션의 수 등을 알 수 있다. 모든 기록 정보는 최적화 성능을 위해 전체 시스템의 세밀한 조정에 사용될 수 있다. 로드 밸런싱 정책과 클러스터 노드의 효율성을 평가하기 위한 기초로서 이러한 결과를 보여주는 리포트가 생성되야만 한다.
현재 클러스터와 로드 밸런서 설정에 필요로 하는 모든 설정은 설정 파일(server.xmlrules.xml)을 조작함으로써 수동으로 이루어 졌다. 자카르타 그룹이 클러스터링과 로드 밸런싱 설정을 관리하기 위해 필요로 하는 설정의 변경을 수행할 수 있는 웹 기반의 클러스터 관리 GUI 툴을 제공한다면 매우 도움이 될 것이다.

참고 자료

Tomcat 5에서의 클러스터링과 로드 밸런싱, Part 1

Tomcat 5에서의 클러스터링과 로드 밸런싱, Part 1

by Srini Penchikala
03/31/2004
번역 허태명
최신 버전의 톰캣 서블릿 컨테이너는 확장성 있고 견고한 웹 어플리케이션을 배치하는데 필수적인 클러스터링과 로드 밸런싱을 제공하고 있다. 이 글의 첫번째 파트에서는 클러스터링과 로드 밸런싱 기능에 관한 설치, 설정, 사용법, 확장에 관한 개괄적인 면을 살펴볼 것이다. 두번째 파트에서는 톰캣 서버에서 클러스터링 인스턴스를 설정하는데 관련된 절차를 보여주는 예제 웹 어플리케이션을 소개하고, 클러스터 환경에서 메모리 복제를 사용하는 세션 퍼시스턴스에 대해 살펴볼 것이다.
톰캣 5 서버는 룰 기반의 로드 밸런서 어플리케이션이 탑재되어 있다. 웹 리퀘 스트를 리다이렉트 시키기 위하여 라운드-로빈과 랜덤 알고리즘을 기반으로 두 개의 간단한 커스텀 로드 밸런싱 룰(룰 API를 상속받은)이 작성되었다. 클러스터 환경 에서 실행되는 예제 웹 어플리케이션의 퍼포먼스 벤치마크가 제공된다. 로드 밸런싱 메카니즘을 살펴보기 위해 다수의 웹 유저를 시뮬레이트해주는 로드 테스팅 툴인 JMeter가 사용되었다.
이 글은 톰캣 서블릿 컨테이너에서의 클러스터링 능력을 보여주는데 주로 촛점을 두고 있기 때문에, 여기서 EJB, JNDI, JMS 객체를 복제하는 J2EE 어플리케이션의 클러스터링에 관해서는 다루지 않을 것이다. EJB와 JMS 클러스터링을 위하여 "J2EE Clustering" 과 "J2EE Clustering with JBoss"을 참조하라

Large-Scale 시스템 디자인

엔터프라이즈 웹 포탈 어플리케이션은 웹 사이트를 방문하는 수 천명의 사용자에게 서비스하기 위해 웹 서비스의 확장성고가용성(High Availability : HA) 을 제공해야 한다. 확장성은 클러스터에 서버를 추가함으로써 늘어나는 사용자의 수를 지원하기 위한 시스템의 능력이다. 고가용성은 기본적으로 시스템에 잉여 자원을 제공하는 것이다. 만약 어떤 클러스터 멤버가 어떠한 이유로 실패한다면, 클러스터의 또 다른 멤버가 투명하게 웹 리퀘스트를 이어받을 수 있다. 클러스터 환경에 웹 포탈 어플 리케이션을 배치하는 것은 우리에게 웹 포탈 어플리케이션에서 요구하는 확장성, 신뢰성, 고가용성을 성취할 수 있는 능력을 준다. 기본적으로, 클러스터링의 주 목적은 시스템에서 1개의 문제점(Single Point of Failure : SPoF)때문에 발생하는 웹 사이트의 어떤 장애도 방지하도록 하는 것이다.
Large-scale 시스템 디자인은 엔터프라이즈 어플리케이션 환경에서 최소한의 다운 타임과 최대한의 확장성을 보증하는 미션-크리티컬한 서비스를 제공한다. 1개의 서버를 운영하는 것보다는 다수의 협력하는 서버를 운영해라. 확장성을 위해 당신은 클러스터 내에 추가적인 머신을 포함해야만 하고, 다운타임을 최소화하기 위해 당신은 클러스터의 모든 컴포넌트에 잉여 자원이 있는지 확인해야 한다. Large-scale 시스템의 주요한 요소는 로드 밸런싱, 결함 허용(fault tolerance), 세션 상태 영속성(session state persistence) 기능을 포함하는 클러스터링이다. 일반적으로 웹 어플리케이션에서 하드웨어 또는 소프트 웨어 기반의 로드 밸런서가 클러스터 내의 어플리케이션 서버 앞단에 위치한다. 이 로드 밸런서들은 웹 트래픽을 적당한 클러스터 멤버로 리다이렉팅 시키고 동시에 서버의 장애요소를 찾아내어 클러스터 노드 사이의 부하를 분배하기 위해 사용된다.

클러스터링

클러스터는 J2EE 어플리케이션에서 마치 하나의 엔터티인 것처럼 투명하게 운영되는 어플리케이션 서버들의 그룹으로 정의된다. 클러스터링은 두 가지 방법으로 구현될 수 있다: 수직 확장(Vertical Scaling)수평 확장(Horizontal Scaling). 수직 확장은 1개의 머신에 운영되는 서버의 갯수를 증가시키는 것이고, 반면에 수평 확장은 클러스터 내에 머신의 갯수를 증가시키는 것이다. 오직 1대의 머신에 비해 클러스터 환경에 포함된 다수의 머신이 있기 때문에 수평 확장이 수직 확장보다 더 신뢰성이 있다. 수직 확장에서는 머신의 프로세싱 파워, CPU 사용과 JVM Heap 메모리의 설정이 얼마나 많은 서버 인스턴스가 한 대의 머신(또한 server-to-CPU ratio로도 알려져 있는)에서 운영될 수 있는지 결정하는 주요한 요소이다.
J2EE 클러스터 내에 있는 서버들은 보통 세가지 옵션 중에 한가지를 사용하여 설정된다. 독립적 접근방식에 있어서 각 어플리케이션 서버는 자신의 어플리케이션 파일들의 복사본을 가지고 자기 자신의 파일 시스템을 가지고 있다. 또 다른 접근법은 공유된 파일 시스템을 사용하는 것이다. 공유된 파일 시스템에서 클러스터는 모든 어플리케이션 서버가 어플리케이션 파일을 획득하기 위해 사용할 단일의 저장장치를 사용한다. 세번째 설정 접근방식은 관리 접근법이라고 불린다. 이 방식에서는 관리 서버가 어플리케이션의 컨텐츠에 관한 접근을 통제하고 적당한 어플리케이션 컨텐츠를 관리되는 서버로 "보내는데(pushing)" 책임이 있다. 관리 서버는 클러스터 내의 모든 서버가 어플리케이션을 사용가능하다는 것을 보장한다. 관리 서버는 또한 어플리케이션이 배치될 때 모든 서버를 업데이트하고 어플리케이션이 제거될 때 모든 서버의 어플리케이션을 제거한다.
클러스터링은 데이터베이스 티어를 포함하여 J2EE 어플리케이션의 다양한 티어에서 이뤄질 수 있다. 어떤 데이터베이스 벤더는 클라이언트(일반적으로 서블릿 컨테이너나 또는 어플리케이션 서버)가 데이터를 가져오기 위해 어떤 데이터베이스에 연결되어 있는지 알 필요가 없도록 클라이언트 투명성을 제공함으로써다수의 데이터베이스 사이에서 데이터 복제를 지원하는 클러스터링된 데이터베이스를 제공한다. JDBC 클러스터링의 예로는 오라클 9i의 Real Application Clusters (RAC)과 Clustered JDBC (C-JDBC)가 있다. RAC는 데이터베이스 커넥션의 fail over를 지원하고 투명하게 fail over된 데이터베이스 노드에 JDBC 커넥션과 데이터베이스 요청을 재연결한다. C-JDBC는 웹 어플리케이션이 JDBC 드라이버를 통해 투명하게 데이터베이스 클러스터에 접근하게 해주는 오픈 소스 데이터베이스 클러스터이다. C-JDBC 구현은 클러스터에서 데이터베이스 노드들 사이의 JDBC 커넥션을 로드 밸런싱할 뿐만 아니라, 보조 데이터베이스 서버로 fail over 처리를 해준다.

톰캣에서의 클러스터링

톰캣 이전 버전(버전 4.1)에서는 서드-파티의 JAR 파일을 사용하여 클러스터링을 할 수 있었다; 그것은 클러스터에서 다수의 톰켓 인스턴스를 운영하기 위하여 설치하고 설정하는 것이 쉽지 않았다. JavaGroups는 오픈 소스 서블릿 컨테이너(톰캣)과 어플리케이션 서버(JBoss)에 클러스터링 기능을 추가해주는 잘 알려진 대안이다. 그러나 톰캣 최신 버전에서 클러스터링은 주요 설치 패키지의 일부가 되었다. 이것은 서드-파티 클러스터링 구현을 톰캣 서버와 연동하기 위한 추가의 노력을 최소화해 준다.
전형적인 클러스터 환경에서 클러스터 내의 서버들은 협력하고 상태를 복제하기 위해 서로 통신을 해야 할 필요가 있다. 이러한 그룹 커뮤니케이션은 point-to-point RMI (TCP-IP) 또는 IP 멀티캐스트를 통하여 이루어 진다. 대부분의 J2EE 어플리케이션 서버들(JBoss, 오라클, 웹로직, 볼랜드와 같은)은 클러스터 내에서 다른 서버로 상태/업데이트/심장박동(heartbeats : 서버의 이상유무)을 전송하기 위하여 모두 IP 멀티캐스트 통신을 사용한다. 이제 톰캣에서 클러스터 멤버들 사이에 통신이 어떻게 이루어 지는지 살펴보자: 모든 클러스터 멤버는 멀티케스트 ping 메세지를 사용하여 서로 통신한다. 각 톰캣 인스턴스는 세션 복제를 위한 IP 주소와 TCP 리스너 포트를 전파하는 메세지를 전송할 것이다. 만약 한 인스턴스가 주어진 시간 단위안에 메세지를 받지 못하면, 그 인스턴스는 다운된 것으로 간주될 것이다.
클러스터링에서 또 다른 잘 알려진 개념은 farming이라고 불리는데, 웹 어플리케이션에서 cluster-wide hot deployment를 제공한다. 서버 farm에서 웹 어플리케이션은 클러스터 내의 오직 한 노드의 어플리케이션 WAR 파일을 복사함으로써 배치된다; farming은 전체 클러스터에서 웹 어플리케이션의 배치를 다룰 것이다. 유사하게 한 개의 클러스터 노드에서 WAR 파일을 제거하는 것은 클러스터의 모든 노드에서 웹 어플리케이션을 제거하게 할 것이다. 톰캣 클러스터링 문서에서는 톰캣의 다음 버전에서 farming 기능을 지원할 것을 언급하고 있다.

로드 밸런싱

로드 밸런싱(또한 high availability switch over으로 알려져 있는)은 서버 부하가 서버 클러스터 내에서 다른 노드들로 로드 밸런싱 정책에 의거하여 분배되도록 하는 메카니즘이다. 한 개의 서버에서 어플리케이션을 실행하기 보다는, 시스템은 어플리케이션 코드를 동적으로 선택한 서버 상에서 실행한다. 클라이언트가 서비스를 요청할 때, 협력하는 서버들 중의 하나(또는 그 이상)이 요청을 수행하도록 선택된다. 로드 밸런서는 클러스터 내에서 요청을 받아들이는 유일한 진입점으로서, 또 개별적인 웹 또는 어플리케이션 서버의 트래픽 관리자로서 역할을 수행한다.
두 가지 잘 알려진 클러스터에서 로드 밸런싱 방법은 DNS round robin하드웨어 로드 밸런싱이다. DNS round robin은 하나의 논리적인 이름을 제공하고 클러스터 내에서 노드들의 어떤 IP 주소를 돌려 받는다. 이 옵션은 비용이 많이 들지 않고, 단순하며 설정하기 쉽다. 하지만 DNS round robin은 서버에 어떤 유연성이나 고가용성도 제공하지 않는다. 반면에 하드웨어 로드 밸런싱은 가상 IP 어드레싱을 통해 DNS round robin의 한계점을 해결한다. 로드 밸런서는 클러스터를 위하여 하나의 IP 주소를 제공하고 그것은 클러스터 내의 각 머신에 매핑된다. 로드 밸런서는 각 요청을 받고 클러스터 내에서 다른 머신들에게 헤더를 다시 써준다. 만약 우리가 클러스터에서 어떤 머신을 제거한다면, 변화는 즉각적으로 나타난다. 하드웨어 로드 밸런싱의 장점은 서버의 유연성 고가용성이다; 단점은 매우 비용이 많이 들고 설정하기 복잡하다는 것이다.
로드 밸런싱을 수행하기 위해 부하를 분배하는 정책을 정의하는 알고리즘은 간단한 라운드 로빈 알고리즘에서 매우 복잡한 알고리즘까지 다양하다. 일반적으로 많이 쓰이는 알고리즘은 다음과 같은 것이 있다:
  • Round-robin
  • Random
  • Weight-based
  • Minimum load
  • Last access time
  • Programmatic parameter-based (로드 밸런서는 입력 인자에 따라 서버를 선택할 수 있다)
로드 밸런싱 알고리즘은 통계적 변수, 속도, 단순성에 영향을 미친다. 예를 들어, weight-based 알고리즘은 "Load Balancing Web Applications."을 참조하면 다른 알고리즘보다 더 긴 계산시간을 가지고 있다.

톰캣에서의 로드 밸런싱

이전 버전의 톰캣에서는 로드 밸런싱 기능이 제공되지 않았다. 아파치 웹서버와 톰캣 서블릿 컨테이너를 함께 연동하는 것은 웹 리퀘스트를 조절하고 로드 밸런싱을 하는 잘 알려진 방법이다. 아파치-톰캣 설정에서 Tomcat Worker로 불리는 톰캣 인스턴스가 로드 밸런싱을 구현하기 위해 설정된다.
톰캣 5는 세 가지 방법으로 로드 밸런싱을 제공한다: JK native connector의 사용, mod_proxymod_rewrite를 아파치 2와 사용, 그리고 balancer 웹 어플리케이션의 사용이다. 이 글에서 우리는 클러스터 내에서 웹 리퀘스트를 다른 노드들로 리다이렉트 시키는 balancer 웹 어플리케이션을 사용하는 세번째 옵션을 설명하는데 중점을 둘 것이다. 로드 밸런서 어플리케이션은 클러스터 내의 다음 이용가능한 멤버로 웹 리퀘스트를 리다이렉트 시키는 서블릿 필터 메카니즘을 사용하는 룰 기반의 어플리케이션이다. 서블릿 필터는 서블릿 2.3 스펙에서 소개되었다. 이 필터는 웹 어플리케이션의 JAAS 인증, 암호화, 로깅, 감사(auditing), 데이터 압축, XML 컨텐츠를 변환하는 XSLT 필터 등과 같은 다양한 작업에 사용된다. 톰캣 밸런서 웹 사이트에 언급된 것과 같이, 밸런서 어플리케이션은 다른 견고한 로드 밸런싱 메카니즘을 대체하기 위해 디자인된 것은 아니다. 그보다 톰캣 밸런서 어플리케이션은 다수의 서버 사이에서 트래픽을 조절하는 단순하고 확장가능한 방법이다. 여러가지 룰을 사용하여 로드 밸런싱이 다양한 방법으로 어떻게 구현되는지 이해하기 위하여 밸런서 어플리케이션에 제공되는 샘플 자바 클래스를 살펴보라.
로드 밸런싱은 다양한 룰과 리다이렉션 URL을 포함하고 있는 룰 설정 파일(rules.xml이라고 불리는)을 생성함으로써 사용 가능해 진다. 밸런서 필터는 rules.xml 파일에 정의되어 있는 것과 같은 순서로 룰을 체크함으로써 리퀘스트를 어디로 리다이렉트할지 결정하기 위해 RuleChain을 체크한다. Rule이 기준에 맞으면, 필터는 검사를 중단하고 리퀘스트를 해당하는 룰에 정의된 URL로 리다이렉트시킨다.

결함 허용(Fault Tolerance)

결함 허용은 클러스터 내의 어떤 서버가 다운되면 가능한한 엔드 유저에게 투명하게 이용가능한 다른 서버로 fail over하기 위하여 계산을 허용하는 시스템의 능력이다. 이상적인 fail over 시나리오는 클러스터 서비스가 리퀘스트를 처리하기위해 서버 인스턴스가 더 이상 사용 불가능할 때를 감지하고 그 인스턴스에 대해 리퀘스트 보내는 것을 멈추는 것이다. 또한 정기적으로 클러스터 멤버가 다시 사용가능한지 알아보도록 점검하고, 그렇다면 자동적으로 그 멤버를 액티브 클러스터 노드 풀에 추가해야만 한다.

톰캣에서의 결함 허용

톰캣 5는 클러스터 멤버가 다운됐을 때를 감지하는 내장된 fail over 메카니즘은 제공하지 않는다. 그러나 희망적으로 톰캣의 다음 버전은 웹 리퀘스트에 대해 서비스할 준비가 되어 있는지 확인하기 위해 특정 클러스터 멤버가 이용가능한지 감지하는데 사용될 수 있는 fail over 기능을 제공할 것이다.
전형적으로 클러스터링 솔루션에서 제공되는 fail over 기능은 두 단계가 있다:
  • Request-level fail over: 만약 클러스터 내의 서버 중의 하나가 다운된다면, 그 이후의 모든 리퀘스트들은 클러스터 내의 나머지 다른 서버로 리다이렉트되야만 한다. 이것은 서버 상태를 추적하고 응답하지 않는 서버에 리퀘스트를 보내는 것을 피하기 위해 heartbeat 메카니즘을 사용하는 것을 포함한다. 우리의 클러스터 설정에서 로드 밸런서로서 사용되는 톰캣 인스턴스는 웹 리퀘스트를 클러스터 내의 다른 노드로 포워딩함으로써 Request-level fail over를 수행할 수 있다.
  • Session-level fail over: 웹 클라이언트는 HTTP 서버에 의해 유지되는 세션을 가질 수 있다. Session-level fail over에서 클러스터 내의 서버 중의 하나가 다운된다면, 클러스터 내의 다른 서버가 최소한의 연속성 손실만으로 최초의 서버에 의해 다뤄진 세션을 가지고 계속 작업을 수행할 수 있어야 한다. 이것은 클러스터 내에 서버간의 세션 데이터의 복제를 포함한다. 세션 복제 능력을 가지고 있는 톰캣 클러스터는 session-level fail over를 수행할 수 있다.

세선 상태 영속성(Session State Persistence)

Fail over와 로드 밸런싱은 클러스터 내에 다른 서버로 세션 상태가 복제될 수 있는 것을 필요로 한다. 세션 상태 복제는 클라이언트 세션이 최초 생성된 원래 서버가 다운됐을 때 클러스터 내의 다른 서버로부터 클라이언트의 세션 정보를 동일하게 얻을 수 있도록 한다. 상태는 시스템 상태이거나 어플리케이션 상태(어플리케이션 상태는 HTTP 세션에 저장된 객체나 데이터를 포함한다)일 수 있다. 세션 복제의 주 목적은 클러스터 멤버가 다운되거나 어플리케이션의 업데이트 또는 시스템 유지보수를 위해 멈췄을 때 세션의 세세한 사항까지 잃지 않도록 하는 것이다.
세션 영속성에 관한한, 클러스터링은 간단한 시나리오가 될 수 있다. 이 시나리오에서 클러스터 멤버는 다른 클러스터 멤버의 세션 상태에 관한 알고 있는 바가 전혀 없다. 그리고 유저 세션은 전적으로 로드 밸런서에 의해 선택된 한 서버에 존재한다. 이것은 세션 데이터가 웹 리퀘스트를 받은 클러스터 멤버에 존재하기 때문에 고착된(sticky) 세션(또한 session affinity이라고 알려져 있는)이라고 불린다.
반면에 클러스터는 세션 상태를 정기적으로 모든(하나 또는 두 개 권장) 백업 클러스터 멤버로 전달해서 각 클러스터 멤버가 다른 클러스터 멤버의 세션 상태를 완벽하게 알고 있도록 구현될 수 있다. 이러한 종류의 세션은 복제된(replicated) 세션으로 알려져 있다.
세션 영속성을 구현하는 세가지 방법이 있다:
  • Memory-to-memory replication
  • File System session persistence, 중앙집중화된 파일 시스템에 세션 정보가 read/write 된다.
  • Database session persistence, 세션 데이터가 JDBC 데이터 저장소에 저장된다.
메모리 세션 복제에서 HttpSession 내의 객체는 상태가 변할 때마다 개별적으로 백업 서버에 직렬화 된다. 반면에 데이터베이스 세션 영속성(Database session persistence)에서 세션에 저장된 객체는 객체들중 어느 하나의 상태가 변할 때 함께 직렬화 된다.
데이터베이스/파일 시스템 세션 영속성의 주요 단점은 대형 객체나 수많은 객체를 HttpSession에 저장할 때 확장성이 제한된다는 것이다. 유저가 HttpSession에 객체를 추가할 때마다, 세션 내의 모든 객체들은 직렬화되고 데이터베이스타 공유된 파일 시스템에 저장된다.

톰캣에서의 세션 복제

현재 버전의 톰캣 서버에서 세션 복제는 세션 상태를 all-to-all 복제하는 것이다. 이것은 모든 클러스터 멤버에게 항상 세션 속성들을 전달하는 것을 의미한다. 이 알고리즘은 클러스터가 작을 때 효율적이다. 대형 클러스터를 위해서 다음 톰캣 릴리즈에서는 하나 또는 아마도 두 개의 백업 서버에만 세션을 저장하는 주-보조 세션 복제 기능을 지원할 것이다.
톰캣에서 세션 복제 메카니즘은 세가지 종류가 있다:
  • 톰캣 5(server/lib/catalina-cluster.jar)에 내장되어 있는 SimpleTcpCluster (org.apache.catalina.cluster.tcp 패키지)과 함께 메모리 복제에서의 사용.
  • 공유된 데이터베이스(org.apache.catalina.session.JDBCStore)에 세션을 저장하는 세션 영속성의 사용.
  • 공유된 파일 시스템(catalina-optional.jar의 일부인 org.apache.catalina.session.FileStore)에 세션 상태 저장하기

J2EE 클러스터를 구현하는데 있어서 고려해야 할 요소

J2EE 클러스터를 디자인할 때 고려해야할 요소는 많이 있다. 다음은 large-scale J2EE 시스템 디자인에서 고려해야할 요소들의 목록이다.(이 목록은 EJB 필수 트레이닝 문서 중 "Creating Highly Available and Scalable Applications Using J2EE"에서 가져왔다.)

클러스터링

  • 어떤 종류의 클러스터가 구현되야만 하는가: 수직 확장 또는 수평 확장인가?
  • 어떤 티어에서 클러스터링이 구현되야만 하는가: 웹서버, 서블릿을 위한 서블릿 컨테이너, JSP, HTTP 세션 객체; 또는 EJB, JMS, JNDI 객체를 위한 어플리케이션 서버 또는 데이터베이스 클러스터링?

로드 밸런싱

  • 서버가 선택될 때(예, 활용성? : affinity): 매 리퀘스트마다, 매 트랜잭션마다, 또는 매 세션마다?
  • 어떻게 서버가 선택되는가(예, 로드 밸런싱 정책): 무작위, 라운드-로빈(round-robin), 가중치 기반(weight-based), 가장 덜 부하가 걸린 서버(least loaded server), 또는 어플리케이션에 의해?
  • 로드 밸런싱이 어디서 이루어 지는가: 한 곳 또는 여러 곳, 클라이언트 단 또는 서버 단?

결함 허용(Fault Tolerance)

  • 어떻게 서버가 문제를 감지하는가?
  • fail over하고 다른 서버로 리퀘스트를 시도할 적당한 시간은 언제인가?
  • 문제가 생긴 노드의 시스템과 어플리케이션의 상태는 어떻게 할 것인가?

세션 상태 영속성(Session State Persistence)

  • 상태는 어떻게 통신되는가?
  • 얼마나 자주 통신하는가?
  • 객체의 상태는 어떻게 구체화(materialize)되는가?
  • 상태 영속성 메카니즘은 효율적인가?
  • 복제된 상태의 일관성이 있는가?
  • 세션 상태를 복제하는데 있어서 어떤 네트웍 제한요소가 있는가?

제안된 클러스터 설정

아래 목록은 제안된 클러스터 환경에서 필자가 달성하기 원했던 주요한 목적이다:
  • 클러스터는 고도의 확장성이 있어야 한다.
  • 클러스터는 결함 허용이 가능해야 한다.
  • 클러스터는 동적으로 설정이 가능해야 한다. 그것은 클러스터를 프로그램적으로(자바 코드를 변경) 보다는 선언적으로(설정 파일을 변경) 관리하기 쉽다는 것을 의미한다.
  • 클러스터는 자동 클러스터 멤버 감지 기능을 제공해야 한다.
  • 클러스터는 메모리 세션 상태 복제를 위해 fail over와 로드 밸런싱 기능을 가지고 있어야 한다.
  • 클러스터는 플러그인 방식/설정하기 쉬운 로드 밸런싱 정책을 가지고 있어야 한다.
  • 클러스터는 클러스터 멤버가 그룹에 합류하거나 떠날 때 그룹 멤버쉽 공지를 수행해야 한다.
  • 멀티캐스트를 통하는 동안 메세지 전송의 손실이 없어야 한다.
  • 클러스터링은 웹 어플리케이션과 서버에 함께 연동되어야 한다. 클라이언트와 서버 양쪽에 투명성을 제공해야 한다. 클라이언트 투명성은 클라이언트가 클러스터링된 서비스나 클러스터가 어떻게 설정되었는지 모르는 것을 의미한다. 클러스터는 각각의 서비스들로 보다는 한 개의 것으로서 확인되고 접속되어야 한다. 서버 투명성은 서버의 어플리케이션 코드가 클러스터 내에 있는지 모르는 것을 의미한다. 어플리케이션 코드는 다른 클러스터 멤버와 통신할 수 없다.

결론

이 글의 파트 2에서 우리는 이러한 목적을 달성하기 위해 클러스터를 배치하는 법(다수의 톰캣 서버 인스턴스를 운영함으로써)을 살펴볼 것이다. 또한 톰캣 5에서 클러스터 아키텍쳐와 세션 복제를 가능하게 하는 설정의 자세한 부분에 관하여 논할 것이다.
Srini Penchikala는 Flagstar Bank의 정보 시스템 주제 관련 전문가이다.