J2EE 클러스터링 두껑 열기 by Wang Yu, August 2005

Discuss this Article

translated by 세브니 16/08/2005 23/09/2005

서문 - Preface #

미션 크리티컬하고 대규모의 어플리케이션들이 점점 Java 2, Enterprise Edition (J2EE) 기반에서 작동이 되고 있다. 금융과 빌링(결제, 청구)과 같은 미션 크리티컬한 어플리케이션들은 고(高)가용성(HA)를 요구한다. (Google과 Yahoo와 같은 대규모의 시스템들은 고가용성을 요구한다.) 요즘 점점 증가하고 있는 상호 연결된 세상에서의 고가용성과 확작성의 중요성은 다들 잘 알고 있는 한 사고로 증명이 되었다: 1999년 6월에 발생한 eBay 의 22시간 서비스 단절 사고로 약 2백 3십만 개의 경매가 중단되었고, eBay 주식 가치가 9.2 퍼센트 떨어졌다.
More and more mission-critical and large scale applications are now running on Java 2, Enterprise Edition (J2EE). Those mission-critical applications such as banking and billing ask for more high availability (HA), while those large scale systems such as Google and Yahoo ask for more scalability. The importance of high availability and scalability in today's increasingly inter-connected world can be proved by a well known incident: a 22-hour service outage of eBay in June 1999, caused an interruption of around 2.3 million auctions, and made a 9.2 percent drop in eBay's stock value.

J2EE 클러스터링은 결함 허용이 되는 고가용성과 확장성을 갖춘 서비스를 제공해 주는 널리 알려진 기술이다. 그러나 J2EE 스펙에서의 지원이 부족하여, J2EE 밴더들은 각자 클러스터링을 구현하고 있으며, 이것이 J2EE 아키텍트와 개발자들에게 많은 수고를 끼치고 있다. 일반적으로 다음과 같은 질문을 할 수 있다:
J2EE clustering is a popular technology to provide high available and scalable services with fault tolerance. But due to the lack of support from the J2EE specification, J2EE vendors implement clustering differently, which causes a lot of trouble for J2EE architects and developers. Following questions are common:

  • 왜 클러스터링 능력을 가진 상업적인 J2EE 서버 제품들은 그렇게 비싼가? (클러스터링 능력이 없는 것과 비교해 10배)
  • Why are the commercial J2EE Server products with Clustering capabilities so expensive? (10 times compared with no clustering capabilities)
  • 왜 스탠드-얼론 J2EE 서버에서 작성된 어플리케이션이 클러스터 환경에서 작동하지 않는가?
  • Why does my application built on stand-alone J2EE server not run in a cluster?
  • 왜 클러스터링 없는 환경에서 빠르게 돌아가는 어플리케이션이 클러스터링 환경에서는 매우 느리게 작동하는가?
  • Why does my application run very slowly in a cluster while much faster in non-clustered environment?
  • 왜 클러스터 어플리케이션이 다른 밴더의 서버에 포팅되지 않는가?
  • Why does my cluster application fail to port to other vendors' server?

한계와 고려 사항을 가장 잘 이해하는 방법은 그 구현방법을 공부해보고 J2EE 클러스터링의 두껑을 벗겨보는 것이다.
The best way to understand the limitations and considerations is to study their implementations and uncover the hood of J2EE clustering.

기초 용어들 - Basic Terminology #

구현에 대해 논의하기 전에, 클러스터링 기술의 기본이 되는 여러가지 개념들과 이슈에 대해 알아보자. 이것이 J2EE 클러스터링 제품의 여러가지 디자인 이슈와 개념을 이해하고, 클러스터링 구현들을 구분하는 여러가지 이슈들의 틀을 마련하여 그 이슈들을 더 잘 이해할 수 있는 토대를 마련하기를 바란다.
It makes sense to understand the different concepts and issues that underlie clustering technology before we discuss the different implementations. I hope this will not only give you the foundation necessary to understand various design issues and concepts in J2EE clustering products, but will also frame the various issues that differentiate clustering implementations and make them easier to understand as well.

확장성 - Scalability#

특정 대규모 시스템에서, 최종 사용자의 수와 행위를 예측하기란 쉬운 일이 아니다. 확장성이란 사용자의 빠른 증가에도 견딜수 있는 시스템의 능력을 말한다. 서버에 의해 다루어지는 동시적인 세션의 수를 확장하는 직관적인 방법은 메모리, CPU, 하드 디스크와 같은 자원을 서버에 추가하는 것이다. 확장성 이슈를 해결하는 또다른 방법으로 클러스터링이 있다. 클러스터링은 대량의 작업을 공유하도록 서버들을 그룹화하고, 논리적으로 하나의 서버인 것처럼 동작한다.
In some large-scale systems, it is hard to predict the number and behavior of end users. Scalability refers to a system’s ability to support fast increasing numbers of users. The intuitive way to scale up the number of concurrent sessions handled by a server is to add resources (memory, CPU or hard disk) to it. Clustering is an alternative way to resolve the scalability issue. It allows a group of servers to share the heavy tasks, and operate as a single server logically.

고가용성 - High Availability#

확장을 위한 (메모리와 CPU 증설과 같은) 단독-서버 솔루션은 장애라는 점때문에 문제점을 가진다. (역자주: not a robust - 잘 깨어지지 않는 것이 아니다) 금융과 빌링 같은 미션-크리티컬한 어플리케이션은 단 1분이라도 서비스 중단을 허용하지 않는다. 이런 서비스들은 어떤 경우라도 적당하고/예측 가능한 응답 속도로 접근이 가능해야 한다. 클러스터링은, 하나의 서버가 장애가 발생해 서비스가 불가능하면, 클러스터 내의 여분의 서버를 제공함으로써 고가용성을 달성하는 솔루션이다.
The single-server’s solution (add memory and CPU) to scalability is not a robust one because of its single point of failure. Those mission-critical applications such as banking and billing cannot tolerate service outage even for one single minute. It is required that those services are accessible with reasonable/predictable response times at any time. Clustering is a solution to achieve this kind of high availability by providing redundant servers in the cluster in case one server fails to provide service.

부하 분산 - Load balancing#

부하 분산은 클러스터링 배후의 주요한 기술중의 하나이다. 이는 요청을 다른 서버에 처리토록 함으로써 고가용성과 나은 성능을 얻는 방법이다. 부하 분산기는 간단한 서블릿이나 플러그인(예를 들면, ip 체인을 사용하는 Linux 서버)에서부터 SSL 가속기가 내장된 비싼 하드웨어까지 어떤 것으로도 가능하다. 요청을 전달하는 것 이외에도, 부하 분산기는 오로지 하나의 서버에서만 사용자 세션이 살아있게 하는 "session stickiness"(역주: 이런 세션의 경우 다음 요청시에 같은 서버로 요청이 전달된다), 장애난 서버에 요청을 전달하지 않도록 하는 "health check" (또는 "heartbeat")와 같은 다른 중요한 작업도 수행한다. 때때로, 부하 분산기는 "장애 극복" 처리에도 참여를 하는데, 나중에 언급될 것이다.
Load balancing is one of the key technologies behind clustering, which is a way to obtain high availability and better performance by dispatching incoming requests to different servers. A load balancer can be anything from a simple Servlet or Plug-in (a Linux box using ipchains to do the work, for example), to expensive hardware with an SSL accelerator embedded in it. In addition to dispatching requests, a load balancer should perform some other important tasks such as “session stickiness” to have a user session live entirely on one server and “health check” (or “heartbeat”) to prevent dispatching requests to a failing server. Sometimes the load balancer will participant in the “Failover” process, which will be mentioned later.

결함 허용 - Fault Tolerance#

높은 수준의 가용한 데이터가 꼭 정확한 데이터를 말하는 것은 아니다. J2EE 클러스터에서, 하나의 서버 개체에 장애가 발생하면, 새로운 요구들이 클러스터의 다른 여분의 서버 개체들에 의해 처리될 수 있기 때문에 여전히 서비스는 가능하다. 그러나 장애가 발생한 서버에서 수행 중이던 요청은 정확한 데이터를 받지 못할지도 모른다. 반면에, 결함 허용 서비스는 어떤 경우의 장애에도 항상 정확한 행위를 보장한다.
Highly available data is not necessarily strictly correct data. In a J2EE cluster, when a server instance fails, the service is still available, because new requests can be handled by other redundant server instances in the cluster. But the requests which are in processing in the failed server when the server is failing may not get the correct data, whereas a fault tolerant service always guarantees strictly correct behavior despite a certain number of faults.

장애 극복 - Failover#

장애 극복은 결함 허용을 달성하는 클러스터링 배후의 또다른 주요 기술이다. 클러스터 내의 다른 노드를 선택함으로써, 이전 노드에 장애가 발생하더라도 처리는 계속될 것이다. 한 노드의 장애 극복은 (어떤 노드가 대신할 것인지) 명백히 기록되어 지거나 다른 서버에 정보를 재발송하는 기반 플랫폼에 의해 자동으로 수행되어 질 수 있다.
Failover is another key technology behind clustering to achieve fault tolerance. By choosing another node in the cluster, the process will continue when the original node fails. Failing over to another node can be coded explicitly or performed automatically by the underlying platform which transparently reroutes communication to another server.

멱등 함수 - Idempotent methods#

멱등 함수는 동일 인수로 반복적으로 호출하여 동일한 결과를 얻는 함수를 말한다. 이런 함수들은 시스템의 상태에 영항을 주지 않기 때문에 걱정없이 여러번 반복해 호출될 수 있다. 예를 들어, "getUserName()" 함수는 멱등 함수인데, "deleteFile()" 함수는 그렇지 않다. 멱등은 HTTP 세션 장애극복과 EJB 장애극복을 논의할 때 중요한 개념이다.
Pronounced “i-dim-po-tent”, these are methods that can be called repeatedly with the same arguments and achieve the same results. These methods shouldn’t impact the state of the system and can be called repeatedly without worry of altering the system. For example, “getUsername()” method is an idempotent one, while “deleteFile()” method isn’t. Idempotency is an important concept when discussing HTTP Session failover and EJB failover.

J2EE 클러스터링이란 무엇인가? - What's J2EE Clustering? #

순진한 질문이다. (역주: 쉽게 답하기 힘든 것을 너무 간단하게 질문하니 그런듯) 그럼에도 나는 여전히 그것에 답하기 위해 몇개의 용어와 그림을 사용하고 있다. 보통 J2EE 클러스터링 기술은 "부하 분산"과 "장애 극복"을 포함한다.
A naive question, isn’t it? But I still use some words and figures to answer it. Generally, the J2EE clustering technology includes “Load balancing” and “failover”.

그림 1: 부하 분산 - Load balancing

그림 1에서, 부하 분산은 동시에 특정 객체를 요청하는 클라이언트가 여럿 존재하는 것을 의미한다. 요청자와 응답자 사이에 위치한 부하 분산기는 원래의 함수와 같은 함수를 가진 여분의 객체에 요청을 전달할 수 있다. 이 방법을 이용해 고가용성과 고성능이 성취될 수 있다.
Shown as figure 1, load balancing implies that there are many client objects which make requests to target objects concurrently. A load balancer which sits between the callers and callees can dispatch the requests to the redundant objects which have the same functions as the original one. High availability and high performance can be achieved in this way.

그림 2: 장애 극복 - Failover

그림 2에서, 장애 극복은 부하 분산과는 다르게 동작하고 있다. 때때로, 클라이언트 객체는 대상 객체에 성공적인 함수 요청을 할 수 있다. 만일 대상 객체가 요청 중에 장애가 발생한다면, 장애 극복 시스템은 이 장애를 탐지하고 다음 요청은 다른 가능한 객체에 전달하게 된다. 결함 허용도 이 방법을 통해 이루어질 수 있다.
Shown as figure 2, failover works differently from load balancing. Sometimes, the client object will make successive method requests to a target object. If the target object fails between the requests, the failover system should detect this failure and redirect the later requests to another available object. Fault tolerance can be achieved in this way.

J2EE 클러스터링에 대해 더 알고자 한다면, 이전의 단순한 질문을 할 것이 아니라, "어떤 객체들이 클러스터될 수 있는가?" 또는 "내가 작성한 J2EE 코드 어디에서 부하 분산과 장애 극복이 발생할 것인가?"와 같은 질문을 하여야 한다. 이 질문들은 J2EE 클러스터링의 원리를 이해하는데 매우 좋은 질문이다. 사실, 모든 객체가 클러스터되는 것이 아니며, 작성된 모든 J2EE 코드에서 부하 분산이나 장애 극복이 발생하지는 않는다! 다음의 코드를 주의깊게 살펴보자:
If you want to know more about J2EE clustering, you should ask more such basic questions as “what types of objects can be clustered?” and “where will load balancing and failover happen in my J2EE code?” Those are very good questions to understand the principle of J2EE clustering. In fact, not every object can be clustered, and not everywhere in your J2EE codes, load balancing and failover can happen! Have a look at the following codes:

그림 3: 예제 코드 - code sample

클래스 A, "business()" 함수의 코드들은 "instance1"이 장애났을 때 클래스 B로 부하 분산 또는 장애 극복될 것인가? 아니다. 부하 부산과 장애 극복을 위해서는 요청자와 응답자 사이에, 함수 호출을 다른 객체로 전달하는, 인터셉터가 존재해야 한다. 클래스 A와 B 객체들은 같은 JVM에서 실행되고 강하게 결합되어 있다. 함수 호출 사이에 어떤 전달 로직을 추가하기란 쉽지 않다.
Will the codes in the “business()” method in “Class A” be load balanced or failed over to another B instance when “instance1” fails? No, I don’t think so. For load balancing and failover, there must be an interceptor between the caller and the callee to dispatch or redirect the method calls to the different objects. The objects of Class A and B are running in the same JVM and coupled tightly. It is hard to add some dispatching logic between methods calling.

그럼, 어떤 종류의 객체들이 클러스터될 수 있는가? - 분산 환경에서 배포될 수 있는 컴포넌트들 만이 클러스터될 수 있다.
So, what types of objects can be clustered? – Only those components that can be deployed in distributed topologies.

그럼, 내가 작성한 J2EE 코드의 어디에서 부하 분산과 장애 극복이 발생할 것인가? - 분산 객체 함수를 호출하는 곳에서만 발생한다.
So, where will load balancing and failover happen in my J2EE code? – Only where you are calling a distributed object’s methods.

그림 4: 분산 객체들 - Distributed Objects

그림 4와 같이, 분산 환경에서는 요청자와 응답자는 운영 컨테이너가 명확한 경계로 분리되어 있다. 그 경계는 다른 JVM일수도 프로세스, 서버 머신일 수도 있다.
In a distributed environment, shown as figure 4, the callers and callees are separated into different runtime containers with obvious boundary. The boundary can be between JVMs, processes or machines.

대상 객체가 클라이언트에 의해 호출되면, 함수는 대상 객체의 컨테이너에서 수행이 된다. (이것이 분산이라 불리는 이유이다) 클라이언트와 대상 객체는 표준 네트웍 프로토콜을 통해 통신한다. 이런 그림을 통해, 부하 분산과 장애 극복을 달성하기 위해 함수 호출 과정에 개입하는 어떤 장치가 존재하는 것을 알 수 있다.
When the target object is called by client, the function is executed in the target’s object’s container (that’s why it is called distributed). Clients and target objects communicate through standard network protocol. These features give chances for some mechanisms to interfere into the method calling route to achieve the load balancing and failover.

그림 4에서, 브라우져는 HTTP 프로토콜을 통해 분산 JSP 객체를 호출할 수 있다. JSP는 웹 서버에서 수행이 되고, 브라우져는 실행에 대해서는 고려하지 않고, 단지 실행 결과만을 원할 뿐이다. 이런 시나리오에서, 브라우져와 웹 서버 사이에 부하 분산과 장애 극복 역할을 수행하는 무언가가 위치할 수 있다. J2EE 분산 기술에는 JSP(Servlet), JDBC, EJB, JNDI, JMS, 웹 서비스 등이 포함된다. 부하 분산과 장애 극복은 이런 분산 함수들이 요청될 때 발생할 수 있다. 다음 장에서 기술들에 대해 자세히 논의해보자.
Shown as figure 4, a browser may call a remote JSP object through HTTP protocol. The JSP is executed in a Web server, and the browser doesn’t care about the execution, it only wants the results. In such scenario, something can sit between the browser and the Web server to achieve the load balancing and failover functions. In J2EE, distribute techniques include: JSP(Servlet), JDBC, EJB, JNDI, JMS, Web services and others. Load balancing and failover can happen when these distributed methods are called. We will discuss the detailed techniques in the next sections.

웹 티어에서의 클러스터링 구현 - Web tier clustering implementation #

웹 티어 클러스터링은 J2EE 클러스터링에서 가장 중요하고 기본이 되는 기능이다. 웹 클러스터링 기술에는 웹 부하 분산과 HTTPSession 장애 극복을 포함한다:
Clustering in the Web tier is the most important and fundamental function in J2EE clustering. Web clustering technique includes: Web load balancing and HTTPSession failover.

웹 부하 분산 - Web Load Balancing#

J2EE 밴더들은 웹 부하 분산을 여러가지 방법으로 구현하고 있다. 기본적으로, 부하 분산기는, 그림 5에서 보는 것과 같이, 브라우져와 웹서버 사이에 위치한다.
The J2EE vendors achieve Web load balancing in many ways. Basically, a Load balancer intervenes between browsers and Web servers, shown as figure 5.

그림 5: 웹 부하 분산 - Web Load Balancing

부하 분산기는 F5 Load Balancer와 같은 하드웨어 제품일 수 있으며, 부하 분산 플러그인을 장착한 다른 웹서버일 수도 있다. ip 체인을 사용하는 리눅스 서버로도 간단히 부하 분산을 잘 수행한다. 어떤 기술이 사용되든, 부하 분산기는 보통 다음과 같은 특성를 지닌다:
A load balancer could be a hardware product such as F5 Load Balancer, and it also could just be another Web server with Load Balancing Plug-Ins. A simple Linux box with ipchains can also perform load balancing very well. Whatever technique it uses, the load balancer normally has the following features:

  • 부하 분산 알고리즘 구현 - 클라이언트 요청이 도착하면, 로드 분산기는 요청을 어떻게 최하부 서버 개체에 전달할 것인지 결정할 것이다. Round-Robin 방식과 Random 방식, 가중치 기반 방식이 주로 사용된다. 부하 분산기는 모든 서버 개체가 같은 업부 부하를 가지도록 고안되었지만, 서버 개체에 전달되는 요청의 수에 기반을 하기 때문에 위의 어떤 알고리즘도 이상적인 분산은 이룰 수 없다. (역주: 모든 요청이 같은 업무 로드로 되어 있지는 않다. 업로 로드가 큰 요청이 특정 서버에 몰린다면 실질적인 부하 분산이 되었다고 볼 수 없다) 어떤 잘 고안된 부하 분산기는 서버에 요청을 전달하기 전에 모든 서버의 업부 부하를 탐지하는 특별한 알고리즘을 구현하고 있다.
  • Implement Load balancing algorithms - When client requests comes, the load balancer will decide how to dispatch the requests to the backend server instances. Popular algorithms include Round-Robin, Random and Weight Based. The load balancer tries to achieve equal work load to every server instances, but none of above algorithms can really get ideal equality because they are only based on the number of requests dispatched to a certain server instance. Some sophisticated load balancer implements special algorithm which will detect every server’ works load before dispatching the requests to the servers.
  • (건강)상태 점검 - 어떤 서버 개체에 장애가 발생하면, 부하 분산기는 이 문제를 인식해야 하고 그것에 더이상 요청을 전달하면 안된다. 또한 부하 분산기는 장애났던 서버가 복구되었는지 감시하다가 요청 전달을 재개할 필요가 있다.
  • Health check - When some server instance fails, the load balancer should detect this failure and never dispatch requests to it any more. The load balancer also needs to monitor when the failed server comes back, and resume dispatching requests to it.
  • 세션 고정(점착) - 거의 모든 웹 어플리케이션은 세션 상태를 유지하며, 간단히는 로그인 유무를 기억하거나, 쇼핑 카트의 품목들을 기억하는 역할을 한다. HTTP 프로토콜은 자체로 상태를 유지하지 않으므로, 세션의 상태는 어딘가에 저장하여 다음에 동일한 웹 어플리케이션의 페이지를 요청하게 되면 검색 상태를 쉽게 가져다 쓸 수 있어야 한다. 부하 분산시에는, 검색 세션이 끝나기 전까지는 같은 서버 개체에 요청을 전달하는 것이 가장 좋은 선택이다. 그렇지 않다면, 어플리케이션이 제대로 동작하지 않을 것이다.
  • Session stickiness - Nearly every Web application has some session state, which might be as simple as remembering whether you are logged in, or the contents of your shopping cart. Because the HTTP protocol is itself stateless, session state needs to be stored somewhere and associated with your browsing session in a way that can be easily retrieved the next time you request a page from the same Web application. When load balancing, it is the best choice to dispatch the request to the same server instance as the last time for a certain browser session. Otherwise, the application may not work properly.

세션 상태가 특정 웹 서버 개체의 메모리에 저장되기 때문에, "세션 고정" 특성은 부하 분산을 위해 중요하다. 그러나, 한 서버 개체가 정전과 같은 이유로 장애가 발생한다면, 이 서버의 모든 세션 상태는 소실될 것이다. 부하 분산기는 이 장애 서버를 탐지해야 하며 어떤 요청도 그 서버에 전달하지 않을 것이다. 세션 상태들이 장애난 서버에 저장되어 있던 요청은 모든 세션 정보를 잃어버려서, 에러를 야기할 것이다. 여기서 세션 장애 극복이 나타난다.
Because the session state is stored in the memory of certain Web server instances, the feature of “session stickiness” is important for load balancing. But, if one of the server instances fails due to some reasons such as power off, all the session state in this server will get lost. The load balancer should detect this failure and won’t dispatch any requests to it any more. But those requests whose session state was stored in the failed server will lost all the session information, which will cause errors. That’s where session failover comes!

HTTPSession 장애 극복 - HTTPSession Failover#

대부분의 유명 J2EE 밴더들은, 서버 개체의 장애에도 세션 상태를 잃지 않게 하여 모든 클라이언트의 요청이 정상적으로 작동되도록, 그들의 클러스터 제품에 HTTPSession 장애 극복을 구현하고 있다. 그림 6에서 보여지듯이,

  • 브라우져가 상태가 유지되는 웹 어플리케이션에 방문하면, (step 1, 2)
  • 이 어플리케이션은 차후 사용을 위해 세션 객체를 메모리에 저장할 것이다; 동시에 세션 객체를 구별할 수 있는 유일한 HTTPSession ID를 브라우져에 보낸다. (step 3) 브라우져는 이 ID를 "쿠키"로 저장하고, 다음에 동일 웹 어플리케이션의 페이지를 방문하게 되면 "쿠키"를 웹 서버에 전달할 것이다.
  • 세션 장애 극복을 지원하기 위해서는, 서버 장애시 세션 소실을 막기 위해, 웹 서버의 세션 객체들은 가끔 특정 장소에 백업되어야 한다. (step 4)
  • 부하 분산기가 장애를 탐지하면, (step 5, 6)
  • 다음의 요청을 동일한 어플리케이션이 설치된 다른 서버 개체에 전달한다. (step 7)
  • 세션 객체가 특정 장소에 백업되었기 때문에, 새 웹 서버 개체는 세션을 복구하여 (step 8) 요청을 제대로 처리할 수 있다.
Almost all popular J2EE vendors implement HTTPSession failover in their cluster products to ensure that all client requests can be processed properly without losing any session state in case of failure of some server instances. Shown as figure 6 , when a browser visits a stateful Web application(step 1, 2), this application may create a session object in memory to store information for later use; And at the same time, send the browser a HTTPSession ID which can identify this session object uniquely(step 3). The browser stores this ID as a “Cookie”, and will send the “cookie” back to Web server when next time it requests a page from the same Web application. In order to support session failover, the session object in the Web server will backup itself somewhere sometime (step 4), to prevent session lost in case of server failures. The load balancer can detect the failure (step 5 ,6), dispatch the sequent requests to another server instance which installed the same application (step 7). Since the session object is backed up somewhere, this new Web server instance can restore the session (step 8) and process the requests properly.

그림 6: HTTPSession 장애 극복 - HTTPSession Failover

위의 기능을 실현하기 위해서는, 다음의 이슈들이 HTTPSession 장애 극복 구현과 함께 다루어져야 한다.
To realize the above functionality, following issues should be taken into HTTPSession failover implementations

  • 전역 HTTPSession ID - 위에서 언급된 것과 같이, HTTPSession ID는 특정 서버 개체의 메모리에 저장된 유일한 세션 객체를 가리킨다. J2EE에서, HTTPSession ID는 JVM 개체에 의존적이다. 하나의 JVM 개체에서 여러개의 웹 어플리케이션을 실행할 수 있고, 각 어플리케이션은 사용자마다의 많은 HTTPSession을 가질 수 있다. HTTPSession ID는 현재 JVM 개체의 관련 세션 객체에 접근하기 위한 키(key)이다. 세션 장애 극복을 구현할 때는, 다른 JVM일지라도 두개의 동일한 HTTPSession ID를 생성해서는 안된다. 왜냐하면, 장애 극복이 발생하게 되면, 한 JVM의 세션들은 특정 장소에 백업되어 복구된다. 그러므로, 전역 HTTPSession ID 장치가 확립되어야 한다.
  • Global HTTPSession ID - As described above, a HTTPSession ID is used to identify an in-memory session object in certain server instance uniquely. In J2EE, HTTPSession ID depends on JVM instances. Every JVM instance can hold multiple Web applications, each of these applications can hold many HTTPSessions for different users. HTTPSession ID is the key to access the related session object in current JVM instance. In session failover implementations, it is required that different JVM instances should not produce two identical HTTPSession Ids, because when failover happens, sessions in one JVM may be backed up and restored in another. So, a global HTTPSession ID mechanism should be established.
  • 세션 상태들을 백업하는 방법 - 세션 상태들을 어떻게 백업하느냐가 J2EE 서버를 다른 서버들과 차별화하는 중요한 요소이다. 여러 밴더들은 이것을 서로 다르게 구현하였으며 다음 섹션에서 이것을 자세히 설명할 것이다.
  • How to backup session states - How to backup the session states is a key factor to make one J2EE server special and outstanding from the others. Different vendors implement it differently and I will explain this in detail in the next sections.
  • 백업 빈도 및 입도(粒度: 입자의 크기) - HTTPSession 상태 백업은, CPU 점유, 네트웍 대역, 디스크 또는 데이터베이스에 쓰기 위한 IO 비용 등을 포함한, 성능 비용을 가진다. 백업 작업의 빈도와 백업 객체의 입도는 클러스터의 성능에 크게 영향을 줄 것이다.
  • Backup frequency and granularity - HTTPSession state backup has performance costs, including CPU cycles, network bandwidth and IO cost of writing to the disk or database. The frequency of backup operations and the granularity of backup objects will impact the performance of the cluster heavily.

데이터베이스 저장을 통한 접근법 - Database persistence approach #

거의 모든 J2EE 클러스터 제품들이 세션 상태를 JDBC 인터페이스를 통해 관계형 데이터베이스에 백업하도록 제시하고 있다. 그림 7에서와 같이, 이 접근법은 서버 개체가 세션의 내용물을 직렬화하여 적당한 시기에 데이터베이스에 기록하게 하는 것이다. 장애 극복이 일어나면, 다른 서비스 가능 서버 개체가 장애 서버를 대체하도록 하여, 데이터베이스로부터 모든 세션 상태값을 복구한다. 객체의 직렬화가 주요한 포인트인데, 메모리 상의 세션 데이터를 영속적이고 운반이 가능토록 해준다. 자바 객체 직렬화에 대해 더 알고자 한다면, 다음을 참조하자. "http://java.sun.com/j2se/1.5.0/docs/guide/serialization/index.html"
Almost all popular J2EE cluster products will let you choose to backup your session state to a relational Database through JDBC interface. Shown as figure 7, this approach is simply to let server instances to serialize the session contents and write to a database at proper time. When failover happens, another available server instance takes the responsibility for the failed server, and restores all session states from the database. Serialization of objects is the key point, which make the in memory session data persistent and transportable. More information about Java Object Serialization, please refer to “http://java.sun.com/j2se/1.5.0/docs/guide/serialization/index.html”.

그림 7: 세션 상태를 데이터베이스에 백업 - Backup Session State to a Database

데이터베이스 트랜잭션은 비싼 자원이기 대문에, 이 접근법의 주요 단점은 세션의 크고, 많은 객체들을 저장할 때의 제한된 확장성이다. 데이터베이스를 통한 세션 저장을 활용하는 대부분의 어플리케이션 서버는 객체 저장을 위해 HTTPSession 사용을 최소화할 것을 주장하고 있지만, 이것은 웹 어플리케이션의 아키텍쳐와 디자인에 제한을 가하게 된다. 특히 HTTPSession을 이용해 캐쉬된 사용자 정보를 저장하고자 할때는 더욱 그렇다.
As database transactions are expensive, the main drawback of this approach is limited scalability when storing large or numerous objects in Sessions. Most application servers that utilize database session persistence advocate minimal use of HTTPSessions to store objects, but this limits your Web application's architecture and design, especially if you are using HTTPSession to store cached user data.

데이터베이스 접근법은 다음과 같은 몇가지 장점을 가진다.
The database approach has some advantages though.

  • 구현이 쉽다. 요청 처리와 세션 백업 처리를 구분함으로써 클러스터 관리를 쉽게 하고 더 강하게(깨지지 않도록) 만들어 준다.
  • It is simple to implement. Separate requests processing from session backup processing make a cluster more manageable and robust.
  • 데이터베이스는 공유가 가능하기 때문에 세션에 대한 장애 극복이 어떤 서버에서든 가능하다.
  • Session can fail over to any other host because database is shared.
  • 전체 클러스터에 장애가 발생하더라도 세션 데이터는 생존이 가능하다.
  • Session data can survive the failure of the entire cluster.

메모리 복제를 통한 접근법 - Memory replication approach #

성능의 이슈때문에, 몇몇 J2EE 서버들은 (톰캣, JBoss, 웹로직, 웹스피어) 다른 구현법을 제공한다: 메모리 복제.
Due to performance issues, some J2EE Servers (Tomcat, JBoss, Weblogic, and Websphere) provide an alternative implementation: in-memory replication.

그림 8: 세션 상태의 메모리 복제 - Memory replication for Session State

메모리 기반의 세션 영속은 데이터베이스 대신 (그림 8에서와 같이) 하나 또는 여러개의 백업 서버의 메모리에 세션 정보를 저장한다. 이 접근법은 좋은 성능 때문에 매우 인기가 좋다. 데이터베이스 접근법과 비교해, 원본 서버와 백업 서버 간의 직접 네트웍 통신은 매우 가볍다. 또한 주목해야 할 것은, 이 접근법에서는 이미 모든 세션 데이터들이 백업 서버의 메모리에 존재하고 있기 때문에, 데이터베이스 영속을 통한 접근법에서의 세션 백업 이후의 "복구" 단계가 필요치 않다는 것이다.
Memory-based session persistence stores session information in the memory of one or more backup servers instead of Database (shown as figure 8). This approach is very popular due to its high performance. Comparing with database approach, direct network communication between the original server and backup servers is really lightweight. And also note that in this approach, the “restore” phase in Database persistence approach is not needed because after session backup, all session data is already in the backup servers’ memory for the coming requests.

"JavaGroups"는 현재 JBoos와 톰캣 클러스러링의 통신 계층으로 사용된다. JavaGroups는 그룹 통신과 관리를 위한 신뢰할만한 툴킷이다. JavaGroups는 "그룹 구성원간 프로토콜"과 "메시지 멀티캐스트"와 같은 주요 기능을 제공하는데, 이것들은 클러스터링 작업에 유용하게 사용된다. "JavaGroups"에 대해 더 알고자 한다면, 다음을 참조하라 . "http://www.jgroups.org/javagroupsnew/docs/index.html"
”JavaGroups” is currently the communication layer of JBoss and Tomcat clustering. JavaGroups is a toolkit for reliable group communication and management. It provides core features such as “ Group membership protocols” and “message multicast”, which is very useful in making clustering work. For more information about “JavaGroups”, please refer to “http://www.jgroups.org/javagroupsnew/docs/index.html”.

톰캣의 접근법 : 다중 서버 복제 - Tomcat’s approach : Multi-servers replication #

메모리 복제는 매우 다양한 형태가 존재한다. 첫번째 방법은 세션 데이터들을 클러스트 내의 모든 노드에 복제하는 것이다. 톰캣 5는 메모리 복제를 이런 방법으로 구현하고 있다.
Many variations of memory replication exist. The first method is replicating the session data across all of the nodes in the cluster. Tomcat 5 implements memory replication in this way.

그림 9: 다중 서버 복제 - Multi-servers replication

그림 9에서처럼, 한 서버 개체의 세션이 변경되면, 세션 데이터들을 모든 다른 서버에 백업할 것이다. 한 서버에 장애가 발생해도, 부하 분산기는 그 서버를 대신할 다른 가용한 서버 개체들 중 어떤 것이라도 선택할 수 있다. 그러나 이 접근법은 확장성 측면에서 몇가지 제약을 가진다. 클러스터내에 매우 많은 개체들이 존재한다면, 네트웍 통신 비용이 무시될 수 없을 것이고, 성능을 크게 떨어뜨릴 것이며 네트웍 소통에도 병목 현상이 나타날 수 있다.
Shown as figure 9, when sessions change in one server instance, it will backup its data to all other servers. When one server instance fails, the load balancer can choose any of other available server instances as its backup. But this approach has some limitations in scalability. If there are too many instances in a cluster, the network communication cost cannot be ignored, it will decrease the performance heavily and network traffic may also be a bottleneck problem.

웹로직, JBoos, 웹스피어의 접근법 : 짝을 이룬 서버들간의 복제 - Weblogic, Jboss and WebSphere's approach : paired servers replication #

성능과 확장성 때문에, 웹로직, JBoss, 웹스피어는 메모리 복제를 위해 다른 방법을 제공하고 있다: 그림 10에서 보여주듯이, 각 서버 개체는 메모리상의 세션 정보를 저장하기 위해 임의의 백업 개체를 선택한다.
For performance and scalability reasons, Weblogic, JBoss and Websphere all provide another way to perform memory replication: each server instance chooses an arbitrary backup instance to store session information in-memory, shown as figure 10.

이런 방법으로, 각각의 서버 개체는 자신만의 짝을 이룬 백업 서버를 가지게 된다. 이 접근법은 클러스터에 더 많은 개체들이 추가될 때에도 확장성 문제를 해소한다.
In this way, every server instance has its own paired backup server instead of all other servers. This approach eliminates the scalability problems when more instances are added to the cluster.

그림 10: 짝을 이룬 서버들간의 복제 - paired servers replication

이 접근법이 높은 성능과 확장성을 가지도록 세션 장애 극복을 구현하고 있지만, 여전히 다음과 같은 제약 사항을 가진다:
Although this approach brings an implementation of session failover with high performance and high scalability, it still has following limitations:

  • 부하 분산기가 더 복잡해진다. 한 서버 개체에 장애가 발생하면, 부하분산기는 장애 서버와 짝을 이룬 백업 서버를 기억하고 있어야 한다. 이것은 부하 분산기의 영역 밖의 일이며, 하드웨어 박스를 이용한 일부 부하 분산기는 이런 환경에서는 사용될 수 없다.
  • It brings more complexity to load balancer. When one server instance fails, the load balancer must remember which instance is the paired backup server of the failed one. This will diminish the scope of load balancers, and some hardware boxes cannot be used in such requirement.
  • 일반적인 요청 처리뿐만 아니라, 서버들은 복제에 대해서도 책임을 져야 한다. 각 서버 개체마다, 복제를 수행하기 위해 CPU 주기가 사용될 것이므로 요청 처리 능력은 줄어든다.
  • In addition to normal request processing, the servers are taking on replication responsibility as well. Per Server instance, request processing capacity is diminished because some of the CPU cycles are now going toward replication duties.
  • 일반적인 경우에도, 세션 장애 극복이 발생하지 않는다면 백업 세션을 저장하기 위해 사용되는 모든 백업 서버들의 많은 메모리들은 낭비된다. 이것은 또느 JVM의 GC 부담을 증가시킬 것이다.
  • In normal processing, a lot of memory which is used to store backup sessions wastes in every backup servers when no session failover happens. This can also increase JVM’s GC overhead.
  • 클러스터내의 서버 개체들은 복제 짝을 형성한다. 세션과 연관이 있는 주서버에 장애가 발생하면, 부하 분산기는 장애극복 후의 모든 요청들을 백업 서버에 보낼 것이다. 주서버 장애 이후에 백업 서버가 받아들이는 요청이 배로 늘어나게 되므로 백업 서버 개체의 성능 문제를 야기할 것이다.
  • The server instances in a cluster form replication pairs. So if the primary server which the sessions is stuck to fails, the load balancer can send all failover requests to the backup server. The backup server will see doubling in incoming requests after the primary fails and this will cause performance problems in the backup server instance.

위의 제약들을 극복하기 위해서, 각 밴더들은 다른 형태로 발전해왔다. 위의 네가지 문제를 극복하기 위해, 웹로직은 각각의 서버에 복제 짞을 이루는 것이 아니라 각 세션에 복제 짝을 정의하고 있다. (역주: 한 서버에는 여러 유저의 세션 정보가 저장되는데, 사용자마다 다른 서버로 복제되도록 정의하여, 장애 극복이 된 이후로는 각 사용자마다 다른 서버로 접근하게 하여 부하를 분산한다는 말인듯) 하나의 서버에 장애가 발생하면, 장애 서버의 세션들은 백업 서버들에 흩어지고, 장애 이후에는 부하가 공평하게 나눠진다.
To overcome above limitations, variations from different vendors come into being. To overcome the 4 th point above, Weblogic defines the replication pairs for each session instead of each server. When one server instance fails, the sessions in the failed server have dispersed backup servers, and load gets evenly spread after failure.

IBM의 접근법 : 집중 상태 (저장) 서버 - IBM’s Approach : centralized state server #

웹스피어는 메모리 복제에 다른 선택을 하고 있다: 그림 11에서 처럼 집중 상태 서버에 세션 정보를 백업한다.
Websphere has an alternative choice to memory replication: Backup Session information to a centralized state server, shown as figure 11.

그림 11: 집중 서버 복제 - Centralized Server Replication

이것은 데이터베이스 솔루션과 매우 흡사하다. 차이점은 전용 "세션 백업 서버"가 데이터베이스 서버를 대신하고 있다는 것이다. 이 접근법은 데이터베이스와 메모리 복제 솔루션의 장점들을 취합한 것이다:
It’s very similar to the database solution. The difference is that a dedicated “Session backup Server” replaces the database server. This approach brings combined advantages from both database and memory-replication solutions:

  • 요청 처리를 세션 백업 처리와 분리하여 클러스터를 더 강하게(깨어지지 않도록) 한다.
  • Separating requests processing from session backup processing makes the cluster more robust.
  • 모든 세션 데이터는 전용 서버에 백업된다. 서버 개체들이 다른 서버들의 세션 데이터를 백업하기 위해 메모리를 낭비할 필요가 없다.
  • All the session data is backed up to the dedicated servers. No need for server instances to waste memory for backing up session data from other servers.
  • 세션 백업 서버가 클러스터내의 모든 노드들에 공유되어 있으므로, 세션은 어떤 개체에서든 장애 극복될 수 있다; 더 중요한 것은, 한 서버에 장애가 발생한 이후에도 요청 부하가 공평하게 나누어질 것이다.
  • Session can fail over to any other instances, since the session backup servers are shared by all nodes in the cluster. So, most of load balancer software and hardware can be the choice for the cluster; and more important, the request loads will spread evenly when one server instance fails.
  • 어플리케이션 서버와 세션 백업 서버간의 소켓을 통한 통신은 무거운 데이터베이스 통신과 비교해 가볍기 때문에, 더 나은 성능을 발휘하고 데이터베이스 솔루션보다 확장이 용이하다.
  • Because the socket communication between Application server and session backup server is lightweight comparing to the heavy database connections, it has better performance and is more scalable than the Database solution.

그러나, 장애 서버의 세션 데이터를 회복하기 위한 "복구" 단계 때문에, 메모리가 직접 짝에 복제되는 솔루션과 비교해서는 성능적으로 비교가 되지 않는다. 또한 추가적인 세션 백업 서버가 관리자를 더 복잡하게 만들고, 백업 서버 자체로 성능 병목 현상을 일으킬 수도 있다.
However, due to the “restore” phase to recover the session data for the failed server, its performance cannot be the same as the solution in which memory is replicated directly between pairs. Also, the additional session backup servers add more complexity to administrators, and it is more likely to form the performance bottleneck in the backup sever itself.

Sun의 접근법 : 특수 데이터베이스 - Sun’s approach : special Database #

Sun의 JES 어플리케이션 서버는, 그림 12에서와 같이, 세션 장애극복를 다르게 구현하고 있다. 외견상으로는, 세션 저장을 위해 관계형 데이터베이스를 사용하고 모든 세션 데이터에 접근하기 위해 JDBC를 사용하기 때문에 데이터베이스 접근법과 같아 보인다. 그러나, 내부적으로 보면, JEUS에서 사용되는, HADB라 불리는, 관계형 데이터베이스는 세션 접근에 매우 효과적이고 메모리 상의 거의 모든 데이터를 저장한다. 그래서, 집중 상태 (저장) 서버와 더 흡사하다고 말할 수 있을 것이다.
Sun JES Application Server implements Session Failover differently, shown as figure 12. From the surface, it looks like the same as the database approach, because it uses a relational database as the Session store and use JDBC to access all the session data. But from the internal, the relational database used by JES, which is called HADB, is optimized for session access specially and stores almost all data in memory. So, you can say it is more close to the approach of centralized state server.

그림 12: 특수 데이터베이스 복제 - Special Database Replication

성능 이슈 - Performance issues #

다음과 같은 상황을 상상해보자: 하나의 웹 서버로 십 여개의 웹 어플리케이션을 운영하고, 각 웹 어플리케이션은 수백명의 동시 사용자에 의해 사용되며, 각 사용자는 특정 어플리케이션 용으로 브라우져 세션 정보를 생성한다. 모든 세션 정보들은 서버 장애시 다른 가용한 서버 개체에서 복구될 수 있도록, 백업될 필요가 있다. 더 나쁜 상황으로 세션들이 계속적으로 변경되는 경우이다: 세션이 생성되거나 소멸될 때, 세션에 속성이 추가되거나 삭제될 때, 속성값이 변경될 때; 속성이 변경되지 않더라도, 세션의 마지막 변경 시간이 (세션이 소멸 여부를 결정하려고) 접근시 변경되었다. 성능은 세션 장애 극복 솔루션에서 아주 큰 이슈이다. 밴더들은 항상 특정 성능을 만족하도록 서버 동작을 패러미터 등으로 조정할 수 있도록 한다.
Think about this: One Web server may host dozens of Web applications, each of which may be accessed by hundreds of concurrent users, and every user will generate his browser sessions to access certain applications. All the session information needs to be backed up in case of the server failure to restore the sessions in another available server instance. And even worse, the sessions are changing from time to time: when sessions are created or expired, when attributes are added to or remove from the sessions and when attributes are modified; even when no attributes are updated, the last modified time of the session is changing when accessing (to decide when to expire the session). So the performance is the big issue in the session failover solutions. Vendors always give you some tunable parameters to adjust the server’s behavior to meet your performance requirement.

세션들을 백업하는 시기 - When to backup sessions #

클라이언트의 요청이 처리되고 동안, 세션 데이터들은 항상 변경이 된다. 성능적인 이슈로 인해, 세션들을 실시간으로 백업하는 것은 바람직하지 않다. 백업 빈도를 결정하는데는 항상 장단점이 존재한다. 백업이 너무 자주 발생하게 되면, 성능에 급격히 영향을 줄 것이다; 그러나 백업 간격이 너무 길면, 그 동안에 발생하는 서버 장애시 잃게되는 세션의 정보들은 더 많아질 것이다. 데이터베이스와 메모리 복제 등, 모든 접근법에 있어서, 다음 사항들이 백업 빈도를 결정하는 주요한 요소들이다:
When client requests are processed, the session data is changing every time. For performance issue, it is not wise to backup sessions in real time. It is really a trade-off to choose the backup frequency. If backup actions take place too frequently, the performance will be impacted sharply; But the longer the interval between two backup actions is, the more session information you will lose if a server failure happens during the interval. In spite of all approaches, including database and memory replication, followings are popular options to decide the backup frequency:

  • 웹 호출 이후에 - 세션 상태는 클라이언트에 응답을 전달하기 전에, 각 웹 요청의 마지막 시점에 저장된다. 이 유형은 장애시에도 세션 상태가 완전히 복구될 수 있도록 가장 잘 보장해 준다.
  • By Web-methods - The session state is stored at the end of each Web request prior to sending a response back to the client. This mode provides the best guarantee that the session state is fully updated in case of failure.
  • 고정된 간격을 두고 - 세션 상태는 고정된 간격 주기로 백그라운드로 저장된다. 이 유형은 세션 상태가 완전히 복구되도록 보장해 주지는 않지만, 상태가 각 요청 이후에 저장되는 것이 아니므로 크게 향상된 성능을 제공할 수 있다.
  • By Constant interval - The session state is stored in the background at the frequency of a constant interval. This mode does not guarantee that session state is fully updated. However, it can provide a significant performance improvement because the state is not stored after each request.

백업 입도(粒度: 입자의 크기) - Backup granularity #

세션을 백업할 때, 또 결정해야 할 사항은 '얼마나 많은 세션 상태가 저장되어야 하는가'이다. 여러 제품들에서 공통적으로 적용되고 있는 것들은 다음과 같다:
When backing up sessions, you still have choices to decide how much of the session state is stored. Some common choices among different products are:

  • 전체 세션 - 항상 전체 세션 상태가 저장된다. 이 유형은 어떤 분산된 웹 어플리케이션이든 세션 데이터를 정확히 저장하도록 가장 잘 보장해 준다. 이 접근법은 간단하며 메모리 복제 솔루션과 데이터베이스 저장 접근법에 모두 적용될 수 있다.
  • Whole session - The entire session state is stored every time. This mode provides the best guarantee that your session data is correctly stored for any distributable Web application. This approach is simple and adopted in both memory replication solution and Database persistence approach by default.
  • 수정된 세션 - 수정된 세션 전체 상태가 저장된다. 세션은, "HTTPSession.setAttribute()" 또는 "HTTPSession.removeAttribute()" 함수가 호출되면 수정된 것으로 간주된다. 속성이 변경될 때마다 이 함수들이 호출되도록 보장해야 한다. 이것은 J2EE 스펙이 정한 바가 아니지만, 이것이 제대로 동작할 필요는 있다. 수정된 세션만을 백업하는 것이 저장되어야 하는 세션의 수를 줄여준다. 세션에서 특정 정보를 읽기만 하는 경우에는 세션을 백업하지 않아도 되며, 전체 세션을 저장하는 유형보다 좋은 성능을 제공한다.
  • Modified session - The entire session state is stored if it has been modified. A session is considered to have been modified if “HTTPSession.setAttribute()” or “HTTPSession.removeAttribute()” was called. You must guarantee that these methods are called every time an attribute is changed. This is not a J2EE specification requirement, but it is required for this mode to work properly. Modified session backup cuts down the number of sessions to be stored. Those requests which only read attributes from sessions will not trigger the session backup actions, that brings better performance than the whole session mode.
  • 수정된 속성 - 전체 세션을 저장하기 보다는 수정된 세션의 속성값만 저장한다. 이것은 백업되는 세션 데이터를 최소화한다. 이 접근법은 최상의 성능을 제공하며 네트웍 소통량을 최소화한다. 이 유형이 제대로 동작하려면, 다음의 몇가지 가이드라인을 따라야 한다. 첫째, "setAttribute()" 호출을 통해 세션 상태가 수정이 되고 수정된 객체가 직렬화되며 백업되도록 해야 한다. 둘째, 속성들 간에 상호 참조되지 않아야 한다. 개별적인 속성 값에 의한 객체 그래프는 별개로 직렬화되어 저장된다. 객체들간에 상호 참조된 객체가 존재하면, 제대로 직렬화되고 반-직렬화되지 않을 것이다. 예를 들어, 그림 13의 메모리 복제 클러스터하에서, 세션에는 "school" 객체와 "student" 객체가 존재하며 "school" 객체는 "student" 객체에 대한 참조값을 가지고 있다. "school" 객체가 어느 순간 수정이 되면 백업 서버에 저장이 될 것이다. 직렬화와 반-직렬화 이후에, 백업 서버에 복구된 "school" 객체는 전체 객체 그래프를 포함할 것이고 참조하고 있는 "student" 객체도 포함할 것이다. 그러나 "student" 객체는 따로 수정이 될 수 없다. 만일 "student" 객체가 수정이 되면, "student" 자체만 백업될 것이다. 직렬화와 반-직렬화 이후에, "student"는 백업 서버의 메모리에 복구 될 것이지만, "school" 객체에 대한 연결고리는 잃게 될 것이다. 이 접근법이 최상의 성능을 발휘하긴 하지만, 웹 어플리케이션의 설계와 디자인에 위의 제약을 주게 된다. 특히나 캐쉬된 복잡한 사용자 정보를 저장하기 위해 세션을 사용한다면.
  • Modified attribute - Only modified session attributes are stored instead of the whole session. This minimizes the session data to be backed up. This approach will bring the best performance and least network traffic. For this mode to work properly, you must follow a few guidelines. First, Call “setAttribute ()” every time the session state is modified and only the modified object is serialized and backed up. Second, Make sure there are no cross-references between attributes. The object graph under each distinct attribute key is serialized and stored separately. If there is any object cross references between the objects under each separate key, they will not be serialized and deserialized correctly. For example, in a memory replication cluster, shown as figure 13, there are a “school” object and a “student” object in the session and the “school” object has a reference to the “student”. The “school” object is modified at sometime and backup itself to the backup server. After serialization and deserialization, the restore version of “school” object in the backup server will include the whole object graph and contain a “student” object with a reference to it. But the “student” object can be modified separately. When it is modified, only the “student” itself will be backed up. After serialization and deserialization, a “student” object is restored in the backup server’s memory, but at this time, it will lose the connection with “school” object. Although this approach brings the best performance, the above limitation is imposed on Web application's architecture and design, especially if you are using the session to store cached complex user data.

그림 13: 세션 복제에서의 상호 참조 - Cross References in Session Replication

다른 장애 극복 구현 - Other failover implementations #

이전 장에서 언급했듯이, 세션이 백업될 때의 입자 크기는 성능에서 매우 중요한 요소이다. 그러나, 데이터베이스 저장과 메모리 복제의 현재 구현은 자바 객체를 전송하기 위해 자바 객체 직렬화를 사용하고 있다. 이것은 큰 발자국을 남겨서 시스템의 성능에 영향을 주고, 웹 어플리케이션의 설계와 디자인에 많은 제약을 가져온다. 몇몇 J2EE 밴더는 가볍고 작은 발자국을 남기는 웹 클러스터링을 구현하기 위해, 클러스터 성능을 향상시키는 매우 작은 단위의 분산 객체 공유 장치를 제공하기 위해 특별한 수단을 강구하고 있다. (역주: footprint의 정확한 의미는 뭘까요?)
As I mentioned in the above section, granularity is very important to performance when sessions are backed up. However, current implementations, both database persistence and memory replication, are all using Java object serialization technology to transfer the java objects. This will bring a big footprint, impact system’s performance and also give a lot of limitations on Web application's architecture and design. Some J2EE vendors seek special means to implement web clustering in a lightweight, small footprint mode and provide fine-granularity distributed-object sharing mechanism to improve cluster performance.

Jini를 사용하는 JRun - JRun with Jini #

JRun 4는 Jini 기술을 바탕으로 클러스터링 솔루션을 만들었다. Jini는 분산 컴퓨팅을 위해 탄생되었으며 하나의 분산 컴퓨팅 공간에 디바이스들과 소프트웨어 컴포넌트들간에 "연합(통합)"할 수 있도록 해준다. Jini는 클러스터링 환경에 유용한 것들을 조회하고, 등록하며, 사용할 수 있는 분산 시스템 서비스를 제공한다. Jini에 기반한 JavaSpace라 불리는 다른 서비스는 클러스터 구현에 적합하도록 객체 처리, 공유, 이동과 같은 모습들을 제공한다. Jini와 JavaSpace에 대한 자세한 정보는 http://java.sun.com/products/jini/2_0index.html를 참조하기 바란다.
JRun 4 has built their clustering solution based on Jini technology. Jini was born for distributed computing and it allows you to create a "federation" of devices and software components in a single distributed computing space. Jini provides the distributed system services for lookup, registration, and leasing which is useful to a clustering environment. Another technology called JavaSpace built on Jini provides features such as object processing, sharing, and migration which are also valuable to a cluster implementation. For more information about Jini and JavaSpace, please refer to “http://java.sun.com/products/jini/2_0index.html”.

분산 캐쉬를 제공하는 Tangosol - Tangosol with Distributed Cache #

Tangosol Coherence™은 클러스터링 환경을 제공하기 위해 대부분의 J2EE 컨테이너에 임베드될 수 있는 분산 데이터 관리 플랫폼을 제공한다. 또한 Tangosol Coherence™은 다른 JVM 개체들 간에 효과적으로 자바 객체들을 공유할 수 있게 하는 분산 캐쉬 시스템을 제공한다. Tangosol에 대한 자세한 정보는, http://www.tangosol.com/를 참조하기 바란다.
Tangosol Coherence™ provides a distributed data management platform which can be embedded into most of popular J2EE containers to provide clustering environment. Tangosol Coherence™ also provides distributed cache system which can share java objects among different JVM instances effectively. For more information about Tangosol, please refer to “http://www.tangosol.com/”.

JNDI 클러스터링 구현 - JNDI clustering implementation #

J2EE 스펙은 모든 J2EE 컨테이너가 JNDI 스펙을 구현하도록 요구하고 있다. J2EE 어플리케이션에서 JNDI의 주요한 역할은 (특정 자원의 간접적인) 위치 지정 계층을 제공하여 자원의 위치에 대해 명확히 알지 못하여도 찾을 수 있도록 한다. 이것이 J2EE 컴포넌트의 재사용성을 높인다.
The J2EE specification requires that all J2EE containers should provide an implementation of the JNDI specification . The primary role of JNDI in a J2EE application is to provide the indirection layer so that resources can be found without being much aware of the indirection. This will make J2EE components more reusable.

클러스터화된 JNDI의 충분한 기능을 보유한다는 것은 J2EE 클러스터에서 중요하다. 대부분의 EJB 접근은 JNDI 트리 상의 홈 인터페이스에 대한 조회로부터 시작된다. 밴더들은 그들의 클러스터 구조에 따라 각각의 JNDI 클러스터링을 구현하고 있다.
Having full-featured clustered JNDI is important for a J2EE cluster, as almost any EJB access starts with a lookup of its home interface in the JNDI tree. Vendors implement JNDI clustering differently, depend on their cluster structure.

전역 공유되는 JNDI 트리 - Shared global JNDI Tree #

웹로직과 JBoss는 클라이언트들이 객체를 조회(lookup)하고 연결(bind)시키기 위해 전역, 공유되는, 클러스터 전체에서 사용가능한 JNDI 컨텍스트를 가진다. 전역 JNDI 컨텍스트에 연결된 객체들은, 서버 개체가 다운되면 IP 멀티캐스트를 통해 클러스터 간에 복제될 것이고, 연결된 객체들은 여전히 조회가 가능하다.
Both Weblogic and JBoss have a global, shared, cluster-wide JNDI Context that clients can use to lookup and bind objects. Things bound to the global JNDI Context will also be replicated across the cluster through IP multicast so that if a server instance is down, the bound objects will still be available for lookup.

그림 14: 전역 공유된 JNDI - Shared global JNDI

그림 14에서 보여지듯이, 실제로 전역 공유된 JNDI 트리는 모든 노드의 지역 JNDI 요소들로 구성된다. 클러스터의 각 노드들은 자신만의 JNDI 네이밍 서버를 가지고 있으며, 클러스터내의 다른 네이밍 서버에 모두 복제된다. 그래서, 모든 네이밍 서버는 다른 네이밍 서버들의 객체들을 트리 상에 보유하게 된다. 이런 부가적인 구조가 전역 JNDI 트리를 가능하게 한다.
Shown as figure 14, the shared global JNDI tree actually consists of all the local JNDI deposits in every node. Each node in a cluster hosts its own JNDI naming server, which replicate everything to the other naming servers in the cluster. Thus, every naming server has a copy of every other naming server’s objects in the tree. This redundant structure makes the Global JNDI Tree highly available.

실제로, 이런 클러스터된 JNDI 트리는 두가지 용도로 사용될 수 있다. 관리자의 역할인 배치를 위해 사용할 수 있다. 한 서버 개체에 EJB 모듈을 배치하거나 JDBC와 JMS 서비스를 설정하면, JNDI 트리상의 모든 객체들은 다른 개체들에 복제될 것이다. 어플리케이션이 수행중에, JNDI API를 이용해 JNDI 트리를 접근하여 객체의 저장 및 조회가 가능하며, 직접 구현한 객체들 또한 전역으로 복제된다.
In practice, this clustered JNDI tree can be used for two purposes. You can use it for deployment which is the task of administrator. After deploying EJB modules or setting JDBC&JMS services in one server instance, all the objects in the JNDI tree will be replicated to other instances. During runtime of applications, your programs can access JNDI tree to store and retrieve objects by using JNDI API, and your custom objects are also be replicated globally.

독립적인 JNDI - Independent JNDI#

JBoss와 웹로직이 모두 전역 공유 JNDI를 채택하고 있지만, Sun의 JES와 IBM 웹스피어 등은 각 어플리케이션 서버에 독립적인 JNDI 트리를 활용하고 있다. 독립적 JNDI 트리를 가진 서버들은 클러스터내 다른 서버들의 존재에 대해 알 필요도 고려할 필요도 없다. 과연 이것이 클러스터된 JNDI를 의미하는 것인가? 대부분의 EJB 접근이 JNDI 트리의 홈 인터페이스에 대한 조회부터 시작되기 때문에, 클러스터된 JNDI 트리 없이는 클러스터링 기능을 제공하지 못한다.
While JBoss and Weblogic all adopt global shared JNDI, Sun JES, IBM Websphere and others utilize an independent JNDI tree for each application server. Member servers in an independent JNDI tree cluster do not know or care about the existence of other servers in the cluster. Does this mean they don’t want clustered JNDI? As almost any EJB access starts with a lookup of its home interface in a JNDI tree, the clustering features would be almost useless without a clustered JNDI tree.

실제로, J2EE 어플리케이션의 성질이 동일해야만 독립적 JNDI 트리의 활용도가 높아진다. 클러스터내의 모든 개체들이 같은 설정을 가지고 같은 조합의 어플리케이션을 배치하였을때, 성질이 동일한 클러스터라고 말한다. 그런 상황하에서, 에이젼트라 불리는 특별한 관리 도구가, 그림 15에서와 같이, 활용도를 높이는데 도움을 줄 수 있다.
Actually, the independent JNDI tree can still have highly available features only if their J2EE application is homogeneous. We call it a homogeneous cluster when all the instances in the cluster have the same settings and have deployed the same set of applications. Under such condition, special admin tools called agent can help achieve the high availability, shown as figure 15.

그림 15: 독립적 JNDI - Independent JNDI

Sun의 JES와 IBM의 웹스피어는 클러스터의 각 개체에 노드 에이젼트를 설치하였다. EJB 모듈을 배치하여 JNDI 서비스와 연결시키면, 관리 콘솔은 모든 에이젼트에 정보를 전달하여 전역 공유 JNDI 트리에서와 같은 효과를 얻는다.
Both Sun JES and IBM Websphere have node agent installed on each instance in the cluster. When deploy EJB modules and binding other JNDI services, the admin console can send commands to all agents to achieve the same effect of the global shared JNDI tree.

그러나 독립적 JNDI 솔루션은 수행중인 어플리케이션에 연결되고 조회되는 임의의 객체들에 대해서는 복제를 지원하지 않는다. 이것에는 몇가지 이유가 존재하는데: J2EE 어플리케이션에서 JNDI의 주요 역할은 외부 자원을 관리하기 위한 위치 지정 계층을 제공하는 것이다. 그런 상황이 (역주: 임의 객체에 대한 복제 상황) 필요하다면, LDAP 서버나 HA로 구현된 데이터베이스를 이용할 수 있다. Sun과 IBM은 이미 클러스터링 기능이 탑재된 LDAP 서버 제품군을 가지고 있다.
But the independent JNDI solution will not support replication for arbitrary objects which are bound and retrieved by running applications. They have reasons for this: the primary role of JNDI in a J2EE application is to provide the indirection layer for administrating external resources, not for runtime data deposits. If such requirements happen, an individual LDAP server or database with HA features can help. Both Sun and IBM have their own individual LDAP server products which are already shipped with clustering features.

중앙집중형의 JNDI - Centralized JNDI#

몇몇 J2EE 제품들이, 네이밍 서버가 하나의 서버에서 운영되어 모든 서버 개체들은 각각의 동일한 EJB 컴포넌트와 관리 객체들을 하나의 네이밍 서버에 등록하는 중앙집중형의 JNDI 솔루션을 이용하고 있다.
A few of J2EE products use centralized JNDI tree solution in which the naming server is hosted on a single server and all servers instances register their same EJB components and other admin objects on the single naming server.

네이밍 서버는 높은 가용성을 제공해야 하는데, 모든 클라이언트에게 공평하게 적용되어야 한다. 모든 클라이언트는 이 유일한 네이밍 서버에서 EJB 컴포넌트를 조회한다. 그러나 이 구조는 항상 복잡한 설치와 관리를 의미하게 되어 대부분의 밴더들이 채택하고 있지 않다.
The naming server itself implements highly available features which is transparent to client. All clients look up EJB components in this single naming server. But this structure always implies complex installation and administration and has thus been abandoned by most vendors.

JNDI 서버에 대한 초기 접근 - Initial access to JNDI server#

클라이언트들은, JNDI 서버에 접근하기 위해 원격 JNDI 서버의 호스트명/IP 주소와 포트 번호를 알고 있어야 한다. 전역과 독립 JNDI 트리 솔루션에서는 여러개의 JNDI 서버가 존재한다. 클라이언트는 어느 JNDI 서버에 처음 접근하게 될까? 부하 분산과 장애 극복을 어떻게 이룰 것인가?
When clients to access the JNDI server, they need to know the hostname/IP address and port number of the remote JNDI server. In global and independent JNDI tree solutions, there are more than one JNDI servers. Which one should clients connect to for the first access to JNDI servers? How to achieve load balancing and failover?

기술적으로, 부하 분산과 장애 극복를 위해 소프트웨어 또는 하드웨어 부하 분산기는 원격 클라이언트와 모든 JNDI 서버들 사이에 존재할 수 있다. 그러나 이런 방식으로 구현한 밴더는 없으며, 간단한 대안들이 존재한다.
Technically, a software or hardware load balancer can sit between the remote clients and all JNDI servers to perform load balancing and failover. But few vendors implement this way, there are many simple solutions.

  • Sun JES와 JBoss는 "java.naming.provider.url" JNDI 설정이 콤마로 분리된 URL 정보를 가지도록 하여 JNDI 클러스터링을 구현하고 있다. 예를 들어, java.naming.provider.url=server1:1100,server2:1100,server3:1100,server4:1100와 같다. 클라이언트는 리스트에 나타난 모든 서버에 차례로 접근하여 가능한 서버를 찾는다.
  • Both Sun JES and JBoss implement JNDI clustering by making the “java.naming.provider.url” JNDI setting accept a list of URLs separated by a comma. For example, java.naming.provider.url=server1:1100,server2:1100,server3:1100,server4:1100. The client will try to get in touch with each server from the list, one after the other, stopping as soon as one server has been reached.
  • JBoss는 자동 발견 기능을 제공하고 있는데, "java.naming.provider.url" 속성이 비었으면, 클라이언트는 네트웍에 멀티캐스트 호출을 함으로써 부트스트랩 JNDI 서버를 찾으려고 한다.
  • JBoss also has implemented auto-discovery features. When property string “java.naming.provider.url” is empty, the client will try to discover a bootstrap JNDI server through a multicast call on the network.

EJB 클러스터링 구현 - EJB clustering implementation #

EJB는 J2EE 기술의 중요한 부분이며 EJB 클러스터링은 J2EE 클러스터링 구현에 있어 가장 핵심적 요소이다.
EJB is an important part of J2EE technology and EJB clustering is the biggest challenge when implement J2EE clustering.

EJB 기술은 분산 컴퓨팅을 위해 탄생하였다. EJB 기술은 분산된 서버들에서 동작이 가능하다. 웹 서버 컴포넌트 또는 리치(rich) 클라이언트은 표준 프로토콜인 RMI/IIOP를 이요애 다른 머신으로부터 EJB에 접근할 수 있다. 로컬의 Java 객체의 함수를 호출하듯이 원격 EJB의 함수를 호출할 수 있다. 이것을 로컬/원격 투명성이라고 부른다.
EJB technology is born for distributed computing. They can be running in independent servers. Web server components or rich clients can access the EJBs from other machines through standard protocol (RMI/IIOP). You can invoke methods on remote EJB just as you would invoke a method on any local Java object. In fact, RMI-IIOP completely masks whether the object you’re invoking on is local or remote. This is called local/remote transparency.

그림 16: EJB 호출 구조 - EJB Invoking Mechanism

위의 그림은 원격 EJB를 호출하는 구조를 보여준다. 클라이언트가 EJB를 사용하려고 할 때, 직접 EJB를 호출할 수는 없다. 다만, 클라이언트는 스텁이라 불리는 로컬 객체를 호출할 수 있다. 스텁은 원객 객체에 대한 대리자 역할을 하며 원격 객체와 같은 인터페이스 구조를 가진다. 스텁은 지역적으로는 함수 호출을 승인해서 이 함수 호출을 네트웍을 통해 원격 EJB의 함수 호출에 위임하여야 한다. 스텁은 클라이언트 JVM에서 수행이 되고 RMI/IIOP를 통해 네트웍에서 실제 객체를 찾는 방법을 알고 있다. EJB에 대한 자세한 정보는, http://java.sun.com/products/ejb/를 참조하라.
The above figure shows the mechanism of invoking remote EJB. When a client wants to use an EJB, it cannot invoke the EJB directly. Instead, the client can only invoke a local object called stub, which acts as a proxy to the remote object and has the same interface as the remote one. The stub is responsible for accepting method calls locally and delegating those method calls to the remote EJBs across the network. Stubs are running within the client JVM, and know how to look over the network for the real object through RMI/IIOP. For detail information about EJB, please refer to “http://java.sun.com/products/ejb/”.

EJB 클러스터링 구현을 설명하기 위해, J2EE 코드상에서 EJB를 사용하는 방법에 대해 살펴보자. EJB를 호출하기 위해, 다음을 수행해야 한다.
Let’s look at how we use EJB in our J2EE code to explain the implementation of EJB Clustering. To make a call to an EJB, you should

  • JNDI 서버로부터 EJBHome 스텁을 조회한다.
  • Look up the EJBHome stub from a JNDI server.
  • EJBHome 스텁을 이용해 EJB 객체를 조회하거나 생성한다; EJBObject 스텁이 생성된다.
  • looks up or create an EJB object using the EJBHome stub; an EJBObject stub returns.
  • OJBObject 스텁을 통해 EJB에 대한 함수 호출을 수행한다.
  • makes method calls against the EJB using the EJBObject stub

JNDI 조회에 대한 부하 분산과 장애 극복은 이전 장에서 이미 언급하였다. (EJBHome과 EJBObject를 포함한) EJB 스텁의 함수 호출동안의 EJB 부하 분산과 장애 극복에 대해서는 밴더들이 다음 세가지 다른 방법을 이용해 구현하고 있다.
Load balancing and failover can happen during JNDI lookup (the 1 st step), which I have already mentioned it in last section. During methods call to EJB stubs (include EJBHome and EJBObject), vendors implement EJB load balancing and failover in following three different ways.

똘똘한 스텁 - Smart stub#

알고 있다시피, 클라이언트는 스텁 객체를 통해 원격 EJB에 접근해 JNDI 트리로부터 스텁 객체를 받을수 있으며, 클라이언트들이 어떤 웹서버에서 받는지 모른체 스텁의 클래스 파일을 다운받을수 있다. 그래서 스텁은 다음과 같은 모습을 가지게 된다:
As you know, client can access the remote EJB through a stub object, this object can be retrieved from a JNDI tree, and it is even possible that clients download the classfile of the stub from any web server transparently. So the stub has the following features:

스텁은 실행시에 자동으로, 프로그램에 의해 생성될 수 있으며 스텁을 정의한 클래스 파일은 실행시에 다운받아질 수 있으므로 클라이언트 클래스패스 또는 클라이언트 라이브러리(JAR)에 속할 필요는 없다.
It can be generated dynamically and programmatically at runtime and the definition of the stub (the classfile) does not necessary needs to be in the classpath of client environment or part of the client libraries (JAR) at runtime (as it can be downloaded).

그림 17: 똘똘한 스텁 - Smart Stub

그림 17에서 나타나듯이, BEA 웹로직과 JBoss는 스텁코드에 클라이언트측에서 투명하게 수행되는 (클라이언트는 이 코드에 대해서 알지 못한다) 몇가지 특별한 행동을 혼합하여 EJB 클러스터링을 구현하고 있다. 이 기술을 "똘똘한 스텁"이라고 부른다.
Shown as figure 17, BEA Weblogic and JBoss implement EJB clustering by incorporating some specific behavior, in the stub code, that will transparently run on the client side (the client code doesn’t even know about this code). This technique is called “smart stub”.

똘똘한 스텁은 앞으로 접근해야 하는 객체들의 목록을 가지고, 대상 객체들의 장애를 감지할 수 있으며, 그 대상으로의 요청들을 전달하기 위한 복잡한 부하분산과 장애극복 로직을 가지고 있다. 또한, (새로운 인스턴스가 추가되거나 제거되는) 클러스터 형상의 변경시에도, 수작업을 통해 설정을 변경시켜주지 않아도 스텁은 새로운 형상에 대한 대상 리스트들을 스스로 변경시킬 수 있다.
The smart stub is really smart that it can contain the list of target instances it can access, it can detect any failure about the target instances, and it also contains complex load-balancing and fail-over logic to dispatch requests to the targets. Furthermore, if the cluster topology changes (for example: new instances added in or removed off), the stub can update itself of the target list to reflect the new topology without manually reconfiguration.

스텁에 클러스터링 구현을 포함시키는 것은 다음과 같은 장점이 있다:
Put the clustering implementation in the stub has following advantages:

  • EJB 스텁은 클라이언트 환경에서 동작하기 때문에, 서버 자원을 많이 절감할 수 있다.
  • Since EJB stub is running inside the client environment, it will save a lot of resources in the server side.
  • 부하분산기가 클라이언트 코드에 포함되어서 클라이언트 생명주기와 밀접히 관련된다. 이는 부하분산기의 장애로 인한 영향을 매우 적게 해준다. 부하분산기가 죽으면, 클라이언트 코드가 같이 죽은 것을 의미하므로 (역주: 전체 시스템에는 영향을 주지 않고 클라이언트를 재시동하면 되므로) 받아들여진다.
  • The load balancer is incorporated in the client code and is highly related with client life cycles. This will eliminate single point of failure of load balancer. If the load balancer dies, it most probably means that the client code is also dead, which is acceptable.
  • 스텁은 동적으로 다운로드 되어지며 스스로 변경이 되어진다. 즉 (역주: 클러스터 형상 변경에 따른) 추가적인 유지가 필요치 않다는 것을 의미한다.
  • The stub can be downloaded dynamically and update itself automatically. That means zero maintenance.

IIOP 런타임 라이브러리 - IIOP Runtime Library#

Sun의 JES 어플리케이션 서버는 EJB 클러스터링을 다른 방법으로 구현하고 있다. 부하분산과 장애극복 로직이 IIOP 런타임 라이브러리에 구현되어 있다. 그림 18에서와 같이, JES는 "ORBSocketFactory" 구현체를 클러스터 인식이 가능하도록 수정하였다.
Sun JES Application Server implements EJB clustering in another way. The load balancing and failover logic are implemented in the IIOP runtime library. For example, JES has modified the “ORBSocketFactory” implementation to let it be cluster-aware, shown as figure 18.

그림 18: IIOP Runtime

"ORBSocketFactory"의 수정된 버젼은 부하분산과 장애극복를 수행하기 위한 모든 로직과 알고리즘을 가지고 있어서, 스텁을 작고 원래의 모습을 유지하게 해준다. (역주: 똘똘한 스텁처럼 스텁이 수정되지 않는다는 의미의 clean?) 구현체가 런타임 라이브러리에 위치하므로, 스텁보다 쉽게 시스템 자원을 획득할 수 있다. 하지만 이 접근법에서는 클라이언트 측에 특별한 런타임 라이브러리가 있어야 하기 때문에, 다른 J2EE 제품과 상호 작동할 경우에는 몇가지 문제점을 가지게 된다.
The modified version of “ORBSocketFactory” has all the logics and algorithms to perform load balancing and failover, which will keep the stub small and clean. Since the implementation is in the runtime library, it can get system resources more easily than stub does. But this approach always requires the specific runtime library in the client side, which will make some troubles when interoperating with other J2EE products.

인터셉터 프락시 - Interceptor Proxy#

IBM의 웹스피어는, 그림 19와 같이, EJB 클라이언트들에 인터셉터 프락시 역할을 하는 위치 서비스 데몬(Location Service Daemon, LSD)를 채택하고 있다.
IBM Websphere employs a Location Service Daemon (LSD), which acts as a interceptor proxy to EJB clients, shown as figure 19.

그림 19: 인터셉터 프락시 - Interceptor Proxy

이 접근법의 경우, 클라이언트는 JNDI를 조회함으로써 스텁을 얻는다. 스텁은 EJB 서비스를 지원하는 어플리케이션 서버가 아닌 LSD와의 접근 정보를 가진다. LSD는 모든 요청을 받고 부하분산과 장애극복에 기초하여 어떤 인스턴스로 요청을 보내야 하는지 결정하게 된다. 이 접근법은 클러스터를 설치하고 유지하기 위해 추가적인 관리자의 작업이 있어야 한다.
Within this approach, a client obtains a stub by looking up from JNDI. The stub contains routing information to the LSD rather than to the application server which hosts EJBs. Then the LSD receives all coming requests and determines where to send them to different instances based on the load balancing and failover policy. This approach will add extra administration works to install and maintain the cluster.

EJB에 대한 클러스터링 지원 - Clustering support for EJBs #

EJB 함수를 호출하려면, 두 종류의 스텁 객체가 필요하다: 하나는 EJBHome 인터페이스이고 하나는 EJBObject 인터페이스이다. 이것은 EJB가 잠재적으로 부하분산과 장애극복을 두가지 레벨에서 수행할 수 있다는 것을 의미한다:
To invoke a method of an EJB, two types of stub objects are involved: one for the EJBHome interface and one for the EJBObject interface. This means that EJBs can potentially realize the load balancing and failover on two levels:

  • 클라이언트가 EJBHome 스텁을 이용해 EJB 객체를 생성하거나 조사할 때
  • When a client create or looks up an EJB object using the EJBHome stub
  • 클라이언트가 EJBObject 스텁을 이용해 EJB에 대한 함수 호출할 때
  • When a client makes method calls against the EJB using the EJBObject stub

EJBHome 스텁에 대한 클러스터링 지원 - Clustering support for EJBHome Stub #

EJBHome 인터페이스는 EJB 컨테이너의 EJB 인스턴스를 생성하거나 조사할 때 사용되고 EJBHome 스텁은 EJBHome 인터페이스를 위한 클라이언트 에이젼트이다. EJBHome 인터페이스는 클라이언트에 대한 어떠한 상태 정보도 가지고 있지 않다. 그래서, 다른 EJB 컨테이너로부터 얻은 같은 EJBHome 인터페이스라도 클라이언트게는 동일시 된다. 클라이언트가 create()나 find() 함수를 호출하면, 홈 스텁은 부하분산과 장애극복 알고리듬에 따라 복제 목록으로부터 서버를 선택하고, 그 서버의 홈 인터페이스에 함수 호출을 전달한다.
The EJBHome Interface is used to create or lookup EJB instances in the EJB container and EJBHome Stub is the client agent for EJBHome Interface. EJBHome interface will not maintain any state information for the client. So, the same EJBHome interface from different EJB containers is identical for the client. When the client issues a create() or find() call, the home stub selects a server from the replica list in accordance with the load balancing and failover algorithm, and routes the call to the home interface on that server.

EJBObject 스텁에 대한 클러스터링 지원 - Clustering support for EJBObject Stub #

EJBHome 인터페이스가 EJB 인스턴스를 생성하면, EJB에 대한 비즈니스 함수 호출을 가능하도록 클라이언트에게 EJBObject 스텁을 반환한다. 시스템은 클러스터내 사용가능한, 빈들이 배포된, 서버들의 목록을 미리 가지고 있지만, EJBObject 스텁에 의한 함수 호출을 임의의 서버 인스턴스 EJBObject 인터페이스에 전달하지는 못한다. 그것은 EJB 종류에 따라 다르다.
When an EJBHome interface creates an EJB instance, it returns an EJBObject stub to the client to let the user make business methods call to the EJB. The system already has a list of all of the available servers in the cluster, to which the beans are deployed, but it cannot route the calls issued by the EJBObject stub to the EJBObject interface on arbitrary server instance, depend on the EJB type.

무상태 세션빈은 아마도 가장 쉬운 경우에 속한다: 상태가 저장되지 않으므로, 모든 EJB 인스턴스는 동일하게 취급된다. EJBObject로부터의 함수 호출은 어떤 서버 인스턴스에서든 부하분산과 장애극복이 가능하다.
Stateless session bean is most probably the easiest case: as no state is involved, All EJB instances are considered identical. So the method invoking from EJBObject can be load-balanced or failed over on any participating server instances.

상태 세션빈은 무상태 빈과는 약간 다르게 클러스터링된다. 알다시피, 상태 세션빈은 계속적인 요청에 클라이언트의 세션 상태 정보를 저장한다. 기술적으로, 상태 세션빈의 클러스터링은 HTTPSession의 클러스터링과 동일하다. 일반적인 경우, EJBObject 스텁은 요청을 다른 서버 인스턴스에 부하분산하지 않는다. 대신, EJBObject가 최초 생성된 인스턴스에 고정된다; 우리는 이 인스턴스를 "주요 인스턴스"라 부른다. (요청이) 처리되는 동안, 상태 정보는 주요 인스턴스에서 다른 서버들로 백업될 것이다. 주요 인스턴스에 장애가 발생하면, 다른 백업 서버가 (처리를) 이어받을 것이다.
Stateful Session Beans are clustered a bit differently from the stateless beans. As you know, Stateful Session Beans will hold session state information for a client in successive requests. Technically, clustering of Stateful Session Beans is the same as clustering of HTTPSession. At normal time, the EJBObject stub will not load balance the requests to different server instances. Instead, it will stick to the instance where the EJBObject is created at first time; we call this instance the “primary instance”. During processing, the state information will backup from the primary instance to other servers. If the primary instance fails, other backup servers will take over.

엔터티 빈은 본질적으로 무상태이지만, 상태를 저장하여 요청을 처리할 수 있다. 모든 정보 데이터들은 엔터티빈 자체의 장치에 의해 데이터베이스에 백업된다. 엔터티빈의 경우, 무상태 세션빈과 같이 부하분산과 장애극복이 쉽게 이루질 것처럼 보인다. 하지만 실제로는, 엔터티빈은 대부분 부하분산과 장애극복이 되지 않는다. 디자인 패턴에서 제안되었듯이, 엔터티 빈은 세션 외관(façade, 퍼사드)에 의해 포장된다. 그래서, 엔터티 빈에 대한 대부분의 접근이 원격 클라이언트로 취급되기 보다는, 수행되는 세션빈에 의해 로컬 인터페이스로 취급된다. 이것이 부하분산과 장애극복을 불가능하게 만든다.
Entity Beans are stateless essentially, although they can process Stateful requests. All the information data are backed up into database by the mechanism of Entity Bean itself. It seems that for Entity Beans, load balancing and failover can be achieved easily just like Stateless Session Bean. But actually, Entity Beans are not load balanced and fail-overed most of time. As suggested by design patterns, entity beans are always wrapped by a session bean façade. Therefore, most access to entity EJBs should occur over local interfaces by in-process session beans, rather than remote clients. This will make load balancing and failover become no sense.

JMS와 데이터베이스 connection에 대한 클러스터링 지원 - Clustering support for JMS and database connection #

J2EE에는 JSP, Servlet, JNDI, EJB 이외에도 여러가지 분산 객체들이 존재한다. 이런 객체들은 경우에 따라 클러스터 구현이 가능할 수도, 불가능할 수도 있다.
There are other distributed objects in J2EE in addition to JSP, Servlet, JNDI and EJB. These objects may or may not be supported in a cluster implementation.

근래에, Oracle RAC와 같은 데이터베이스 제품들은 클러스터링 환경을 제공하고 다수의 복제된, 동기화가 가능한 데이터베이스 인스턴스를 배포할 수 있다. 그러나, JDBC는 상태정보를 매우 많이 가지는 프로토콜이어서(트랜잭션 상태가 서버와 클라이언트간에 소켓을 통해 직접 결합된다), 클러스터링하기가 매우 어렵다. JDBC 연결이 끊어지면, 끊어진 연결과 관련된 JDBC 객체들은 소멸된다. 클라이언트 코드에는 재연결을 위한 행위가 필요해진다. BEA 웹로직은 재연결 절차를 쉽게 하도록 JDBC 멀티 풀을 사용하고 있다.
Currently, some database products, such as Oracle RAC, support clustering environment and can deploy multi replicated, synchronized database instances. However, JDBC is a highly stateful protocol and in which transactional state are tied directly to the socket between the server and the client, so it is hard to achieve clustering. If a JDBC connection dies, a ll JDBC objects associated with the dead connection will also be defunct. The re-connection action is needed in the client code. BEA Weblogic uses a JDBC multipool to eases the reconnection process.

JMS는 대부분의 J2EE 서버에 의해 지원되지만, 완전히 지원되지는 않는다. 부하분산과 장애극복은 JMS 브로커에 의해서만 구현이 되며, 대부분의 제품들은 JMS Destination의 메시지들에 대한 장애극복 기능을 가지고 있지 않다.
JMS is supported in most of J2EE servers, but not fully supported. Load balancing and failover is implemented only for JMS broker, and few products have the failover functions for messages in JMS Destinations.

J2EE 클러스터링에 대한 신화들 - Myths about J2EE clustering #

장애극복을 통해 에러들을 모두 피할 수 있다 -- 부정적
Failover can avoid errors completely. -- Negative #

JBoss 문서를 보면, 하나의 장을 통해 "진짜 HTTP 세션 복제가 필요하냐?"라며 묻는다. 그렇다, 때때로 장애극복 기능이 없는 고가용성 솔루션이 채택되며 가격또한 싸다. 또 더 나아가, 장애극복 기능은 생각보다 아주 튼튼하지 않다.
In the document of JBoss, there is a whole section to warn you “do you really need HTTP sessions replication?” Yes, sometime a high availability solution without failover is acceptable and cheap. And further more, the failover feature is not as strong as you expected.

세상에 진짜 장애극복이 발생하였나? 몇몇의 사람들은 장애극복을 통해 에러가 회피되었다고 생각한다. 세션 장애극복이 없으면, 세션 데이터들은 서버에 장애가 발생하면 사라지며 에러를 발생시킨다; 세션이 장애극복 되는동안, 세션들은 백업으로부터 복원될 수 있고 요청들은 다른 서버 인스턴스에 의해 처리가 가능하다. 클라이언트는 장애의 사실을 알지 못할 것이다. 분명 이러해야 하지만, 이것은 제한적이다.
What on earth does failover bring to you? Some of you may think that failover can avoid errors. You see, without session failover, session data is lost when a server fails and causes errors; while with session failover, sessions can be restored from the backup and requests can be processed by another server instance, the client even isn't aware of the failure. That may be true, but it’s conditional!

이 기사에서 "장애극복"를 정의할 때, 장애극복가 발생할 때의 조건을 먼저 정의하였다: "함수 호출 간에". 이말은, 원격 객체에 대한 연속적인 함수 호출을 하는 경우, 장애극복은 첫번째 함수 호출이 성공적으로 끝나고 두번째 함수 요청이 보내지기 전에만 가능하다는 것을 의미한다.
Remind that when I defined “failover”, I defined a condition for when the failover will happen: “between the method calls”. It means if you have two successive methods to a remote object, the failover will only happen after the first method call is finished successfully and before the second method request is sent out.

그럼, 서버내에서 함수에 대한 중간 처리 과정중에 원격 서버의 장애가 발생하면 어떻게 될까? 답은 그 함수가 "멱등"하지 않다면, 대부분의 경우 처리는 멈출 것이고 클라이언트에 에러 메시지를 보일 것이다. 함수가 멱등하다면, 똘똘한 몇몇 부하분산기는 이 함수들을 재요청하여 다른 인스턴스에서 장애극복하게 한다.
So, what will happen if the remote server fails when the methods are in the middle of processing in the server? The answer is: the process will stop and the client will see error messages in most cases, unless the methods are idempotent (defined in the “basic terminology” section).Only if the methods are idempotent, some load balancers are smart enough to retry these methods and failover them to other instances.

왜 "멱등"이 중요한가? 클라이언트는 어느 서버에서 요청이 수행되는지, 그것에 장애가 발생했는지 알지 못한다. 함수가 이제 시작되었는지 다 끝나가는지, 클라이언트는 절대 알지 못한다! 함수가 멱등하지 않다면, 동일한 함수에 대한 두번의 요청은 시스템 상태를 두번 바꿀 것이고 시스템은 일관되지 않은 상태에 놓일 것이다.
Why is “idempotency” important? Because the client never knows where the execution the request was in when the failure occurred. Has the method just been initiated or it is almost finished? A client can never determine it! If the method is not idempotent, two invokings of the same method will alter the system state twice and the system will be in an inconsistent situation.

트랜잭션 상의 모든 함수들이 멱등하다고 생각할지도 모르겠다. 모든 것이 종료된 후 장애가 발생한다면, 트랜잭션은 롤백이 될 것이고, 모든 트랜잭션 상태 변화는 취소될 것이다. 그러나 모든 원격 함수 호출을 트랜잭션으로 처리하지는 않는다. 서버에서 트랜잭션이 커밋된 이후에 네트웍 장애가 클라이언트에 전달된다면 어떻게 될것인가? 클라이언트는 서버의 트랜잭션이 성공인지 아닌지 전혀 알지 못한다.
You might think that all methods that are placed in a transaction are idempotent. After all, if failure happens, the transaction will roll back, and all transactional state changes will be undone. But the truth is that the transaction boundary may not include all the edges of remote methods invoking. What if the transaction commits on the server and the network crashes on the return trip to the client. The client would not know whether the server’s transaction succeeded or not.

중요한 어플리케이션에서, 모든 함수들을 "멱등"하게 하는 것은 불가능하다. 그래서, 장애극복으로 에러를 줄일 수는 있지만, 모두 없앨 수는 없다! 서버 인스턴스들이 동시에 100명의 사용자의 요청을 처리하는 온라인 상점의 예를 들어보자. 서버에 장애가 발생하면, 세션 장애극복을 하지 않는다면 모든 사용자의 세션 정보를 잃을 것이며 100 사용자는 불만을 나타낼 것이다; 세션 장애극복을 이용한다면, 서버 장애 시점에 단지 20명의 요청이 처리중이었다면, 그들만이 장애에 대해 불만을 가지게 될 것이다. 나머지 80명의 사용자는 처리중에 잠시 멈춘 것으로 생각할 것이다. 이 사용자들은 자신들은 알지못한채 세션 정보들이 복구될 것이다. 그래서, 다음과 같은 장단점을 고려해야 한다:
In serious applications, to make all the methods idempotent is impossible. So, you can only reduce errors by failover, but not avoid them! Take an online store website for example, suppose every server instance will handle 100 online users’ request at any time. When one server fails, the solution without session failover will lose all the users’ session data and anger all the 100 users; while in the solution with session failover, only 20 users’ requests are in processing when the server fails and only these users are angry about the failure. All the other 80 users are just in the thinking time or between the methods. These users get their session failed over transparently. So, you should trade off from following considerations:

  • 20 사용자와 100 사용자 불만 간의 영향의 차이
  • The different impact between anger 20 users and 100 users.
  • 장애극복 기능을 가진 것과 그렇지 않은 제품간의 가격 차이
  • The different cost between products with failover and without failover.

단독 서버에서의 어플리케이션은 클러스터 구조에 쉽게 이전될 수 있다 -- 부정적
Stand-alone applications can be transmit transparently to a cluster structure. -- Negative! #

몇몇 밴더에서 자신의 J2EE 제품이 가지는 유연성에 대해 광고하고 있지만, 그들을 믿지 말라! 실제로, 시스템 디자인 시점에서의 클러스터링을 준비하고 개발과 테스트를 포함한 모든 단계에 영향을 주어야 한다. (역주: 클러스터링을 고려해야 한다)
Although some vendors announce such flexibility for their J2EE products, don’t trust them! Actually, you should prepare for the clustering at the beginning of system design and impact all the phases including development and testing.

Http 세션 - Http Session#

이미 언급한 바와 같이, 세션 장애극복을 위한 장치에 따라 클러스터 환경에서의 HTTPSession 사용에는 많은 제약이 따른다.

  • 첫번째 중요한 제약은 HTTPSession에 저장된 모든 객체들이 직렬화되어야 하는데, 이것은 어플리케이션 디자인과 구조를 제한하게 된다. 몇몇 디자인 패턴과 MVc 프레임워크는 직렬화되지 않는 (서블릿 컨텍스트, 로컬 EJB 인터페이스, 웹서비스 참조값 과 같은) 객체를 HTTPSession에 저장하고 있는데, 이런 디자인은 클러스터에서 동작하지 않는다.
  • 두번째로, 객체 직렬화와 반직렬화는 데이터베이스 저장 기법에서 특별히 성능에 문제를 야기한다. 이런 환경에서는, 세션에 많은 또는 큰 객체를 저장하는 것은 피해야 한다. 메모리 복제 기법을 사용하게 되면, 이전에 언급하였듯이, HTTPSession에 상호 참조되는 속성이 없도록 조심해야 한다.
  • 클러스터 환경에서 주요한 다른 차이점은 HTTPSession 상의 특정 속성이 변경되면 "setAttribute()"를 호출해야 한다. 이 함수는 독립 서버 환경에서는 선택적이다. 이 함수의 목적은 수정된 속성과 수정되지 않은 속성을 구분하기 위한 것이며, 이를 통해 시스템은 성능 향상을 위해 세션 장애극복시 필요한 데이터만을 백업할 수 있다.
In a cluster environment, there are many restrictions to HTTPSession usage as I mentioned before, depending on different mechanism your application server uses for session failover. The first important restriction is that all objects stored in the HTTPSession must be serializable which will limit the design and structure of the application. Some design patterns and MVC framework will use HTTPSession to store some non-serializable objects (such as Servlet context, Local EJB Interface and web services reference), such designs cannot work in a cluster. Secondly, object serialization and de-serialization are very costly in performance especially in the database persistent approach. In such environment, storing large or numerous objects in the session should be avoided. If you have chosen a memory replication approach, be careful about the limitation on cross-referenced attributes in HTTPSession as I mentioned before. Another major difference in cluster environment is you are required to call “setAttribute ()” method whenever any attribute under HTTPSession is modified. This method is optional in stand alone system. The purpose of this method is to separate modified attributes from those untouched, so that the system can backup only necessary data for session failover to improve performance.

캐쉬 - Cache#

내가 경험한 거의 모든 J2EE 프로젝트들이 성능 향상을 위해 객체 캐쉬를 사용했으며, 모든 범용 어플리케이션 서버들은 어플리케이션을 더 빠르게 하기 위해 어느정도의 캐싱을 제공하고 있다. 그러나 그러한 캐쉬는 주로 단독 서버 환경을 위해 고안된 것이고, 하나의 JVM 인스턴스에서만 작동하도록 되어 있다. 우리는 특정 객체들이 너무 무거워서 새롭게 하나 더 생성하는 비용이 큰 경우에 캐쉬를 사용한다. 그래서 추가적으로 생성하지 않고 객체 인스턴스를 재사용할 수 있도록 객체 풀을 유지한다. 객체의 생성보다 캐쉬 유지 비용이 싼 경우에만 성능 향상을 얻을 수 있다. 클러스터 환경에서, 각 JVM 인스턴스는 자신만의 캐쉬를 가지고 있고, 모든 서버 인스턴스에 일관된 (역주:왜 inconsitent이지?) 상태를 제공하기 위해 서로간에 동기화되어야 한다. 때때로 이러한 종류의 동기화는 캐쉬를 전혀하지 않은 경우보다 더 나쁜 성능을 가져오기도 한다.
Almost every J2EE project I experienced used object caching to improve performance, and all popular application servers provide extra degrees of caching to enable faster applications. But these caches are typically designed for a standalone environment, and can only work within one JVM instance. We need cache because some objects are so heavy that creating a new one will cost much. So we maintain an object pool to reuse the object instances without further creation. We gain performance only if the maintenance of the cache is cheaper than objects creation. In a clustered environment, each JVM instance will maintain its own copy of the cache, which should be synchronized with others to provide inconsistent state in all server instances. Sometimes this kind of sync will bring worse performance than no caching at all.

정적 변수들 - Static variables#

J2EE 어플리케이션을 디자인할 때, 아키텍트들은 디자인 패턴을 많이 사용한다. "싱글톤"과 같은 몇몇 디자인 패턴은 많은 객체들간에 상태를 공유하기 위해 정적 변수를 사용한다. 이 접근법은 단독 서버에서는 잘 동작하지만, 클러스터 환경에서는 문제를 야기한다. 클러스터의 모든 인스턴스는 각각의 JVM 인스턴스에 있는 정적 변수의 복사본을 가지고 있어야 하는데, 이것이 패턴 사용의 목적을 벗어나는 경우가 있다. 정적 변수를 사용하는 한가지 예제는 전체 온라인 사용자 수에 대한 통계치를 저장하는 것인데, 정적 변수에 사용자 수를 저장하고, 사용자가 들어오고 나갈 때마다 증가 또는 감소시키면 된다. 이 어플리케이션은 단독 서버에서는 매우 잘 작동하지만, 클러스터에서는 불가능하다. 클러스터에서 가장 바람직한 방법은 데이터베이스에 모든 상태 정보를 저정하는 것이다.
When design J2EE applications, design patterns are popular among architects. Some design pattern such as “Singleton” will use a static variable to share a state among many objects. This approach works well on a single server, but fails in a cluster. Each instance in the cluster would maintain its own copy of the static variable in its own JVM instance, thereby breaking the mechanism of the pattern. One example for the usage of static variable is to keep statistics about total number of online users. One easy way is to store the number in a static variable, increasing and decreasing it when users are in and out. This application works absolutely fine on a single server, but fails on a cluster. A preferable way workable with cluster is to store all state data to a database.

외부 자원 - External resource#

J2EE 스펙에서는 추천되지 않은 것이지만, 외부 I/O 조작이 여러가지 용도로 사용되고 있다. 예를 들면, 어떤 어플리케이션은 파일 시스템을 사용자에 의해 업로드된 파일들을 저장하기 위해 사용하고, 또는 동적인 설정 XML 파일을 생성하기도 한다. 클러스터 환경에서 어플리케이션은 여러 인스턴스들 사이에 이런 파일들을 복제할 방법이 없다. 클러스터 환경에서의 해결법은 가능한 외부 파일을 사용하기 보다는 데이터베이스를 사용하는 것이다. 또는 파일들의 중앙 저장소 역할을 하는 SAN을 사용하는 것이다. (역주: SAN, Storage Area Network)
Although not recommended by the J2EE specification, the external I/O operations are used for various purposes. For example, some applications use file systems to save uploading files by users, or create dynamic configuration XML files. In a cluster the application server has no way of replicating these files across to other instances. To work in a cluster, the solution is to use the database in place of external files, if possible. One could also choose SAN as central deposits for files.

특별한 서비스들 - Special Services#

단독 서버 모드에서만 의미가 있는 특별한 서비스들이 존재한다. Timer 서비스가 좋은 예인데, 일정한 간격으로 정기적으로 작동을 한다. Timer 서비스는 - 로깅 파일 로테이팅, 시스템 데이터 백업, 데이터베이스 일관성 검사, 불필요한 데이터 삭제 등과 같은 - 관리 업무를 자동으로 수행하기 위해 때로 사용된다. 몇개의 이벤트 기반 서비스들도 클러스터 환경에 이식하기가 어렵다. 전체 시스템의 시작 지점에서 수행되는 초기화 서비스들이 좋은 예이다. Email 알림 서비스 또한 특정 경고 상태에서만 발생하는 좋은 예이다.
There are some special services which only make sense in the stand-alone mode. Timer services are good examples of such services, which will happen regularly and based on constant interval. Timer services are often used to perform administrative tasks automatically, such as logging file rotation, system data backup, database consistence checking and redundant data cleaning up. Some event based services are also hard to migrate to a cluster environment. The initial services are good examples which will happen only at the beginning of whole system. Email notification services are also such examples which are trigged by some warning conditions.

이런 서비스들은 요청에 의한 것이 아니라 이벤트에 의해 동작하고, 딱 한번만 수행되야 한다. 이런 서비스들은 클러스터 환경에서의 부하분산과 장애극복을 불가능하게 만든다.
These services are trigged by events instead of requests, and should only be executed only once. Such services will make the load balancing and failover make little sense in a cluster.

어떤 제품들은 이런 서비스들에 대해서도 클러스터링을 제공한다. 예를 들면, JBoss는 단 한번만 서비스를 수행하도록 모든 서버 인스턴스들을 조정하는 "클러스터 가능한 싱글톤 기능"을 제공한다. 선택한 제품에 따라, 이런 특별한 서비스들은 어플리케이션을 클러스터 구조에 이식하는데 장애물이 되곤 한다.
Some products have prepared for such services. For example, JBoss uses “clustered singleton facility” to coordinate all the instances to guarantee to execute these services once and only once. Based on your product platform you choose, those special services may be an obstacle to migrate your applications to a cluster structure.

분산 구조가 밀집 구조보다 유연하다 -- 아마도 아니다
Distributed structure is more flexible than collocated one? -- Maybe Not! #

J2EE 기술은, 특히 EJB는, 분산 컴퓨팅을 위해 태어났다. 분리된 비즈니스 기능들, 재사용 가능한 원격 컴포넌트들이 여러 계층의 어플리케이션을 인기있게 하였다. 그러나 모든 것을 분산으로 처리하지는 않을 것이다. 어떤 J2EE 아키텍트는 웹 게층과 EJB 계층을 밀접하게 위치시키는 것이 더 좋다고 생각한다. 이런 논의는 계속해서 되고 있다. 자세히 알아보자.
J2EE technology, especially EJB, is born for distributed computing. Decoupled business functionality, reused remote components make multi-tier applications popular. But we won’t make everything distributed. Some J2EE architects think it better that the Web tier and EJB tier should be collocated closely. These kinds of discussion are still going on. Let me explain more.

그림 20: 분산 구조 - Distributed Structure

그림 18에서처럼, 이것은 분산된 구조이다. 요청이 도착하면, 부하분산가는 이 요청을 다른 서버의 다른 웹 컨테이너에 전달한다. EJB를 호출하는 요청이라면, 웹 컨테이너는 다른 EJB 컨테이너에 EJB 요청을 재 전달할 것이다. 이런 식으로, 요청들이 두번 부하분산되고 장애극복되어 진다.
Shown as figure 18, it is a distributed structure. When requests come, load balancer will dispatch them to different web containers in different servers. If the requests include EJB invokes, the web container will re-dispatch the EJB invokes to different EJB containers. Such, requests are load balanced and failed over twice.

일부의 사람들은 분산 구조를 낮추어 본다. 그들이 지적하는 것은 다음과 같다:
Some people look down on the distributed structure. They have pointed out:

  • 두단계의 부하 분산은, 일을 공평히 분배할 수 없기 때문에, 필요치 않다. 모든 서버 인스턴스들은 자체로 웹 컨테이너와 EJB 컨테이너를 모두 가지고 있다. EJB 컨테이너가 다른 인스턴스의 웹 컨테이너로부터 받은 요청을 처리하게 하는 것은 동일 서버내에서의 요청과 비교해 장점이 없다.
  • The second load balancing is not necessary, because it cannot assign tasks more evenly. Every server instance will has its own web container and EJB container. To make the EJB container to process requests from other instance’s web container shows no advantages compared to inner invoking happened inside server instances.
  • 두단계의 장애극복은, 가용성을 높이지 못하므로, 필요치 않다. 대부분의 밴더들은 동일한 서버에 있는 웹 컨테이너와 EJB 컨테이너가 같은 JVM 인스턴스들 공유하도록 J2EE 서버를 구현하고 있다. 그렇기 때문에, EJB 컨테이너에 장애가 발생하면 같은 JVM 인스턴스를 사용하는 웹 컨테이너도 같이 장애가 발생한다.
  • The second failover is not necessary, because it cannot improve availability. Most vendors implement their J2EE servers in such a way that web container and EJB container within the same server share the same JVM instance. If the EJB container fails, under most circumstances, the Web container in the same JVM instance will also fail at the same time.
  • 성능 저하를 가져온다. 여러 EJB를 호출하는 어플리케이션 함수를 가정해 보자. 각 EJB를 부하분산한다면, 어플리케이션이 여러개의 서버 인스턴스들에 흩어지게 되고, 불필요한 서버들간의 통신이 많이 필요하게 될 것이다. 또한 이 함수에 트랜잭션이 필요하다면, 트랜잭션 범위가 여러 서버 인스턴스들을 포함하게 될 것이고 심각한 성능 저하를 가져오게 된다.
  • Performance will degrade. Imagine now in one method of your application you may be invoking a couple of EJBs, if you load balance on every one of those EJBs, you're going to end up with instances of your application spread out across the multi server instances; you're going to have a lot of server-to-server cross talk that's unnecessary. And more, if your method is under a transaction, your transaction boundary will include many server instances which will impact performance heavily.

실제로는, 대부분의 밴더들(Sun JES, 웹로직, JBoss 등)은 같은 서버의 EJb 컨테이너를 먼저 선택하도록 EJB 부하 분산을 처리하고 있다. 그림 19에서와 같이, 이 방법을 쓰면 (웹 컨테이너에 대한) 요청에 대해 한차례만 부하 분산을 하고 같은 서버에서 추가적인 서비스들을 처리한다. 이 구조를 밀집 구조라고 말한다. 기술적으로 밀집 구조는 분산 구조의 한가지 유형이다.
At the runtime actually, most vendors (include Sun JES, Weblogic and JBoss) will optimize the EJB load balancing mechanism to let requests first choose the EJB container in the same server. In this way, shown as figure 19, we load balance only at the first level of requests (web container), and then have subsequent services end up on that same server. This structure is called collocated structure. Technically, collocated structure is the special case of distributed one.

그림 21: 밀집 구조 - Collocated Structure

한가지 흥미있는 의문 사항은, 대부분의 배포가 밀집 구조안에서 이루어지므로 성능을 약간이라도 향상시킬 수 있도록 원격 인터페이스보다는 로컬 인터페이스를 사용해야 하는게 아닌가 하는 것이다. 물론 그렇게 할 수 있다. 그러나 기억해야 할 것은, 로컬 인터페이스를 사용하게 되면, 웹 컴포넌트와 EJB는 IIOP/RMI가 아닌 직접 함수 호출하는 방식으로 매우 밀접하게 결합된다. 부하분산과 장애극복 조정자가 로컬 인터페이스 호출에 끼어들 여지가 없어져서, "WEB+EJB" 처리가 한꺼번에 부하분산과 장애극복 대상이 된다.
One interesting question is that, since most deployment are evolved as collocated structure at the runtime, why not use local interface instead of remote interface, and this will improve performance quite a bit. Of course you can. But remember, when using local interface, Web components and EJB are coupled tightly, and make method invoking directly instead of IIOP/RMI. The load balancer and failover dispatcher have no chance to intervene with local interface call, the “Web+EJB” process is load balanced and failover as a whole.

그러나 불행히도, 클러스터에서 로컬 인터페이스를 사용하는 것은 대부분의 J2EE 서버에서 제약을 가진다. EJB가 로컬 인터페이스를 가진 로컬 객체이더라도, 직렬화되지 못한다. 그래서 로컬 참조값을 HTTPSession에 저장하지 못하도록 제한된다. Sun JES와 같은 제품은 로컬 EJB를 약간 다르게 취급하여 직렬화하여 HTTPSession에서 사용할 수 있게 한다.
But unfortunately, using local interface in a cluster has some limitations on most J2EE servers. EJBs are local objects with local interfaces, but they are not Serializable. So the limit is that the local references are not allowed to be stored in HTTPSession. Some products, such as Sun JES, treat local EJBs differently and make them Serializable and can be used in HTTPSession as you will.

다른 흥미있는 의문 사항은: 밀집 구조가 인기있고 좋은 성능을 내는데, 왜 분산 구조가 필요한가? 모든 일에는 원인이 존재하기 나름이다. 가끔, 다음과 같은 경우에는 분산 구조를 쓸 수 밖에 없다.
Another interesting question is: Since collocated structure is popular and has good performance, why we need distributed structure? Like in most cases, things happen for a reason. Sometime, the distributed structure is not replaceable:

  • EJB가 웹 컨테이너에서만 호출되는 것이 아니라, 리치 클라이언트에서도 호출된다.
  • EJB is not only for web container, rich clients are also the consumers.
  • EJB 컴포넌트들과 웹 컴포넌트들이 다른 보안 레벨을 가지고 있어서, 물리적으로 분리될 필요가 있다. 그래서, 방화벽으로 EJB 컴포넌트들이 수행되는 가장 중요한 서버를 보호할 수 있다.
  • EJB components and Web components may be in different security levels, and need to be separated physically. So, firewall can be setup to protect the most important machines on which EJB components are running.
  • 웹과 EJB 계층이 극도로 비대칭한 경우 분산 구조가 더 나은 선택이 될 수 있다. 예를 들어, 어떤 EJB 컴포넌트들은 매우 복잡하고 자원을 많이 사용하여 비싼 큰 서버에서 돌려야만 하는데 반해, (html, JSP, Servlet) 웹 컴포넌트들은 싼 PC 서버에서 돌려도 될만큼 간단하다. 이런 환경에서, 전용 웹 서버를 통해 클라이언트 요청을 받도록 하고, 정적 데이터들과 간단한 웹 컴포넌트들을 매우 빠르게 서비스한다. 큰 서비는 복잡한 컴퓨팅에만 사용하고 투자를 많이 한다.
  • Extreme unsymmetricalness between Web and EJB tier will make the distributed structure a better choice. For example, some EJB components are so complex and resource consuming, that they can only run in some expensive big servers; on the other hand, the Web components (html, JSP and Servlet) are simple enough that cheap PC servers will be satisfied. Under such condition, dedicated Web servers will be used to accept client connection requests, and serve static data (HTML and images) and simple Web components (JSP and Servlet) very quickly. The big servers are only used for complex computing and make the best use of the investment.

결론 - Conclusion #

클러스터링은 단독 서버 환경과는 다르다. J2EE 밴더들은 클러스터링을 서로 다르게 구현하고 있다. 큰 규모의 시스템을 구축하려면 프로젝트 초기에 J2EE 클러스터링을 준비해야 한다. 요구사항을 잘 만족하는 J2EE 제품을 선택해야 한다. 클러스터 환경에서 사용 가능한 서드-파티 소프트웨어와 프레임웍을 선택해야 한다. 또한, 클러스터링을 통해 고통받기 보다는 이득을 얻을수 있도록 구조를 잘 디자인 해야 한다.
Clustering is different from the stand-alone environment. J2EE vendors implement clustering differently. You should prepare for J2EE clustering at the beginning of your projects in order to build a large scale system. Choose proper J2EE product which is well suitable to your requirements. Choose proper third-party software and frameworks to make sure they are cluster-aware too. Then, design proper architectures which will really benefit from clustering instead of suffering.

About the author#

Wang Yu presently works for GPE group of Sun Microsystems as a Java technology engineer and technology architecture consultant. His duties include supporting local ISVs, evangelizing and consulting on important Java technologies such as J2EE, EJB, JSP/Servlet, JMS, Web services technologies. He can be reached at yu.wang@sun.com.

PRINTER FRIENDLY VERSION


Add new attachment

Only authorized users are allowed to upload new attachments.

List of attachments

Kind Attachment Name Size Version Date Modified Author Change note
gif
1.gif 10.5 kB 1 08-Jun-2007 16:22 DongGukLee
« This page (revision-1) was last changed on 08-Jun-2007 16:17 by DongGukLee