SSISO Community

시소당

[웹로직] 클러스터 애플리케이션의 세션 관리

클러스터 애플리케이션의 세션 관리

by Jon Purdy
2005/05/02

개요

클러스터 애플리케이션에는 신뢰할만하고 효율적인 HTTP 세션 관리가 필요합니다. 유감스럽게도 클러스터 환경으로 바꾸면 세션 관리에 몇 가지 문제점이 발생됩니다. 이 문서에서는 이러한 문제점에 대해 설명하고 해결책과 권장 예제를 제시합니다. 이 문서에 포함된 BEA WebLogic Server 및 Tangosol Coherence*Web의 세션 관리 기능에 대해서도 살펴봅니다.

기본 용어

HTTP 세션("세션")은 웹 애플리케이션 내의 일련의 사용자 상호 작용을 망라합니다. "세션 상태"는 사용자별 정보 모음입니다. 세션 상태는 일정 기간 동안 유지되는데, 일반적으로 사용자의 첫 번째 상호 작용부터 시작되어 마지막 상호 작용 이후 대략 30분 정도 후에 끝납니다. 세션 상태는 임의적인 "세션속성(session attribute)" 모음으로 구성되며, 각 세션 상태는 Java 객체로서 이름으로 식별됩니다. "Sticky load balancing"은 특정 사용자의 요청을 동일한 서버에 지속적으로 전달하는 방식으로, 사용자 요청을 일련의 서버에 배포하는 동작을 말합니다.

Coherence는 클러스터 애플리케이션에 실시간으로 완전히 일치하는 데이터 공유를 제공하는 Tangosol의 데이터 관리 제품입니다. Coherence*Web은 Coherence의 일부로 포함된 세션 관리 모듈입니다. HTTP 세션 모델("session model")은 Coherence*Web이 물리적으로 세션 상태를 표시하는 방법을 말합니다. Coherence*Web에는 3가지 모델이 있습니다. Monolithic 모델은 모든 세션 상태를 단일 엔티티로 저장하며 모든 속성을 단일 동작으로 serialize 및 deserialize합니다. Traditional 모델은 모든 세션 상태를 단일 엔티티로 저장하지만 속성을 개별적으로 serialize 및 deserialize합니다. Split 모델은 Traditional 모델을 확대한 개념으로 커다란 세션 속성을 독립적인 물리적 엔티티에 분리시킵니다. 이러한 모델의 애플리케이션은 이 문서의 나중 단원에서 설명합니다.

Figure 1
그림 1. 세션 모델

클러스터 환경에서 데이터 공유

세션 속성을 여러 JVM에서 처리하려면 세션 속성을 serialize할 수 있어야 합니다. 이것은 클러스터링 요구 사항입니다. 세션 속성의 일부 필드를 transient로 선언하여 논-클러스터(non-clustered)로 만들 수 있습니다. 그러면 세션 속성의 모든 필드를 serialize할 수 있어야 하는 요구 사항이 없어지는 반면 이러한 속성이 백업 서버로 완전히 복제되지 않을 것임을 의미하기도 합니다. 이러한 접근 방법을 따르는 개발자는 이 속성 필드가 상실되더라도 애플리케이션이 일관된 방식으로 작동될 수 있도록 아주 신중하게 해야 합니다. 대부분의 경우 이러한 접근 방법은 단순히 모든 세션 속성을 serialize할 수 있는 객체로 변환하는 것보다 마무리가 좀 더 복잡합니다. 하지만 세션에서 아주 방대한 양의 사용자별 데이터를 캐시하는 경우 유용한 패턴일 수 있습니다.

J2EE 서블릿 사양(버전 2.2, 2.3 및 2.4)은 클러스터에서 공유해서는 안되는 서블릿 컨텍스트에 대해 설명합니다. WebLogic Server는 기술된 대로 이 사양을 구현합니다. 싱글톤(singleton) 데이터 구조로서 서블릿 컨텍스트에 의존하는 논-클러스터(non-clustered) 애플리케이션을 클러스터 환경으로 이동하면 포팅 문제가 발생합니다. 일반적으로 애플리케이션이 J2EE 사양을 따르도록 하는 것이 모든 개발자 팀의 목표여야 함에도 불구하고 Coherence*Web은 클러스터 컨텍스트 옵션을 지원합니다.

클러스터 환경에서 발생하는 보다 미묘한 문제는 객체 공유 문제입니다. 논-클러스터(non-clustered) 응용 프로그램에서 두 세션 속성이 공통 객체를 참조하는 경우 공유 객체를 변경하면 두 세션 속성의 일부로서 표시됩니다. 그러나 이것은 클러스터 애플리케이션에서 흔한 경우는 아닙니다. 컴퓨팅 리소스의 불필요한 사용을 막기 위해 대부분 세션 관리 구현은 요청 시 세션 속성을 개별적으로 serialize 및 deserialize합니다. WebLogic Server와 Coherence*Web(Traditional 및 Split 세션 모델)은 모두 기본적으로 이런 방식으로 작동합니다. 공통 객체를 참조하는 두 세션 속성을 개별적으로 deserialize하면 공유된 공통 객체는 두 번 인스턴스화됩니다. 공유 객체 동작에 의존하고 쉽게 수정할 수 없는 애플리케이션의 경우, Coherence*Web은 전체 세션 객체를 단일 동작으로 serialize 및 deserialize하는 Monolithic 세션 모델 옵션을 제공합니다. 그러면 처음부터 클러스터링을 염두에 두지 않고 설계된 애플리케이션에 대한 호환성이 제공됩니다.

프로젝트에서 서로 다른 웹 애플리케이션(및 BEA WebLogic Portal에서 서로 다른 포틀릿 간도 가능) 간 세션 데이터 공유가 필요한 경우가 많이 있습니다. 이때 제기되는 문제는 웹 애플리케이션마다 일반적으로 자체의 클래스 로더가 있다는 점입니다. 따라서 별개의 웹 애플리케이션 간에 객체를 쉽게 공유할 수 없습니다. 각각 자체의 트레이드 오프 세트를 사용하여 이러한 상황을 피해갈 수 있는 일반적인 두 가지 방법이 있습니다. 개발자가 Java CLASSPATH에 공통 클래스를 지정하여 구성이 약간 더 복잡해지더라도 여러 애플리케이션에서 이러한 클래스의 인스턴스를 공유하도록 할 수 있습니다. 다른 방법은 Coherence*Web을 사용하여 클래스 로더 범위 내에서 세션 데이터를 공유합니다. 각 웹 애플리케이션은 동일한 JVM 내에서 실행 중이더라도 별개의 클러스터 멤버로 처리됩니다. 이러한 접근 방법은 웹 애플리케이션 간 결합이 보다 느슨해지지만(serialize된 클래스가 공통 serialVersionUID를 공유한다고 가정) 클러스터 멤버 간 전송 시 객체를 serialized-deserialized해야 하기 때문에 성능에 영향을 줍니다.

Figure 2
그림 2. 클러스터링(serializing-deserializing)을 통해 웹 애플리케이션 또는 포틀릿 간 데이터 공유

신뢰성 및 가용성

애플리케이션이 사용자에 대한 올바른 동작을 표시하려면 사용자의 세션 상태를 제대로 보관해야 합니다. 일부 가용성에 대한 고려 사항은 애플리케이션 디자인 단계에서 제기되고 클러스터 및 논-클러스터(non-clustered) 애플리케이션 모두에 적용됩니다. 예를 들어, 애플리케이션은 사용자 작업이 멱등(idempotent)임을 확인해야 합니다. 즉, 애플리케이션은 실수로 HTML 폼을 두 번 제출한 사용자를 처리할 수 있어야 합니다. (게다가 서버가 요청을 처리하는 중간에 실패하는 경우 WebLogic Server는 자동으로 idempotent HTTP 요청을 장애 조치할 수 있습니다.)

고정 로드 밸런싱을 사용하면 세션 상태에 대한 모든 업데이트가 단일 서버에서 이루어지기 때문에(동시성 관리가 상당히 단순화됨) 일반적으로 동시 세션 업데이트에 관련된 문제를 방지할 수 있습니다. 이로써 이전 요청이 완전히 처리되기 전에 사용자가 새로운 요청을 제출하더라도 사용자 요청이 오버랩되지 않는다는 이점이 있습니다. HTML 프레임을 사용하면 이렇게 복잡하지만 동일한 일반적 패턴이 적용됩니다. 단 하나의 표시 요소가 세션 상태를 변경하고 있는 것은 아주 확실합니다.

동시 요청이 있을 수 있는 경우 Coherence*Web은 단일 서버가 독점적으로 액세스하도록 세션을 잠금으로써 세션 상태(여러 서버에 걸쳐 있더라도)에 대한 동시 변경을 관리합니다. Coherence*Web을 사용하여 개발자는 세션 액세스를 한 번에 하나의 서버(기본값) 또는 한 번에 하나의 스레드로 제한할지 여부를 지정할 수 있습니다.

일반적으로 모든 세션 속성은 가능하면 불변의 객체로 처리해야 합니다. 그래야 개발자가 속성을 변경하는 경우 의식적으로 인식을 할 수 있습니다. 변경 가능한 객체에서 속성을 변경하려면 두 가지 단계가 필요합니다. 먼저 속성 객체의 상태를 변경하고 그 다음 setAttribute()를 호출하여 변경된 속성 객체로 세션을 수동으로 업데이트합니다. 이것은 속성 값이 변경되면 애플리케이션에서 항상 setAttribute()를 호출해야 한다는 것을 의미합니다. 그렇지 않으면 WebLogic Server는 변경된 속성 값을 백업 서버로 복제하지 않습니다. Coherence*Web은 세션에서 검색된 변경 가능한 모든 속성을 추적하므로 setAttribute()를 호출하지 않더라도 자동으로 이러한 속성을 업데이트합니다. 이로써 클러스터 환경에서 클러스터링이 작동하도록 설계되지 않은 애플리케이션을 도와줄 수 있습니다.

WebLogic Server에서 세션 상태는 일반적으로 두 개의 서버 즉, 주 서버와 백업 서버에 보관됩니다. 고정 로드 밸런서는 각 사용자 요청을 지정된 주 서버로 보내고, 세션 상태에 대한 로컬 변경 사항을 모두 백업 서버로 복사합니다. 주 서버가 실패할 경우 다음 요청은 백업 서버로 라우팅되고 사용자의 세션 상태에는 영향을 주지 않습니다. 이 접근 방법이 매우 효율적인 반면(특히 이 방법은 서버 오류 이후 복제 작업으로 인해 클러스터가 제 기능을 못하게 되지는 않음) 몇 가지 단점이 있습니다. 세션이 업데이트되면 세션 상태가 복사되기 때문에 세션 업데이트 사이에 주 서버와 백업 서버 양쪽에서 오류(또는 사이클링)가 발생할 경우 세션 상태를 잃어버리게 됩니다. 이런 문제가 발생하지 않도록 하려면 WebLogic Server 인스턴스 클러스터를 사이클링할 때 각 서버가 재시작하는 사이에 30분을 기다려야 합니다. 이 30분 동안 사용자가 방문할 공산이 커져 세션 복제가 일어날 수 있습니다. 더욱이 이 간격이 세션 시간 초과에 걸리기라도 하면 사용자가 반환하지 않았더라도 세션 상태가 삭제됩니다.

서버가 실패하거나 사이클링되는 경우 세션 데이터를 자동으로 재배포하는 Coherence*Web에서는 이러한 사이클링 간격이 필요하지 않습니다. Coherence는 "위치 투명성(location transparency)" 덕분에 노드에 오류가 발생했다고 해서 데이터 가시성이 영향을 받지는 않습니다. 하지만 노드 오류가 중복에는 영향을 미치기 때문에 새로운 백업 복사본을 만들어야 합니다. 대부분의 Coherence*Web 구성에서는 클러스터 크기에 상관없이 두 대의 시스템(주 서버 및 백업 서버)이 각 세션 데이터를 관리합니다. Coherence는 이러한 구성을 사용하여 하나의 장애 조치 전환을 주어진 지점에서 시간 내에 처리할 수 있습니다. 하나의 서버가 실패하는 경우, 현재의 장애 조치 프로세스가 완료된 후에 다음 서버에서 오류가 발생한다면 데이터는 손실되지 않습니다. 최악의 시나리오는 각 서버에 대량의 세션 데이터가 있고 클러스터 크기가 작은 경우입니다. 그러면 재밸런싱하는 데 1-2분이 걸릴 수 있습니다. 클러스터 크기를 늘리거나 서버당 데이터 저장 용량을 줄이면 장애 조치 성능이 향상됩니다. 상용 서버의 대규모 클러스터에서는 장애 조치 프로세스가 완료되는 데 1초도 안 걸릴 수 있습니다. 특히 중요한 애플리케이션의 경우 백업 시스템 수를 늘리면 Coherence가 관리할 수 있는 동시 발생적인 오류 수가 늘어납니다.

클러스터 애플리케이션에서 serialization하려는 요구는 오류 발생의 새로운 소지를 제공합니다. 단일 세션 속성의 serialization 오류가 발생하면 일반적으로 남아있는 세션 속성이 백업 서버로 복사되지 않도록 하여 전체 세션이 손실되는 결과가 발생할 수 있습니다. Coherence*Web은 serialize할 수 없는 객체는 로컬 서버 인스턴스에 보관하는 반면 serialize할 수 있는 객체만 복제함으로써 이러한 상황을 피해갑니다.

마지막으로, 부하가 높은 상태에서 WebLogic Server는 네트워크 정체로 인해 세션 속성 변경 내용을 상실할 수 있다는 점을 알아야 합니다. 로그에는 손실된 속성에 관한 정보가 포함되며, 이 정보는 다음과 같이 고가용성 계획에 가장 중요한 일면을 제공합니다. 장애 조치와 장애 복구(failback)가 예상대로 작동하는지 확인하려면 부하가 꽉 찬 상태에서 모든 애플리케이션 컴포넌트를 테스트해야 합니다. 많은 애플리케이션에서는 부하가 99%라도 별 어려움이 없지만 시스템이 완전히 꽉 채워지면 애플리케이션 가용성을 실제로 테스트해 볼 수 있습니다.

확장성 및 성능

클러스터 환경으로 옮길 때 세션 크기를 중요하게 고려해야 합니다. 메모리 사용량은 애플리케이션이 클러스터링되든 아니든 상관없는 요소이지만, 클러스터 애플리케이션 역시 큰 세션으로 인한 CPU 증가 및 네트워크 부하를 고려해야 합니다. 메모리 내의 세션을 사용하는 논-클러스터(non-clustered) 애플리케이션은 세션 상태를 serialize-deserialize할 필요가 없지만 클러스터 애플리케이션은 세션 상태가 업데이트될 때마다 그렇게 해야 합니다. 세션 상태를 serialize한 다음 네트워크 상으로 전송하는 것은 애플리케이션 성능에 중요한 요소가 됩니다. 이런 등등의 이유 때문에 일반적으로 BEA는 세션 크기를 수 킬로바이트 정도로 제한할 것을 권장합니다.

Coherence*Web의 Traditional 및 Monolithic 세션 모델은 동일한 제한 요소를 가지며, Split 세션 모델은 큰 HTTP 세션을 효율적으로 지원하도록 명시적으로 설계되었습니다. 단일 클러스터 캐시 엔트리를 사용하여 작은 세션 속성을 모두 포함하면 세션 또는 세션의 작은 속성을 액세스하거나 업데이트하는 경우 네트워크 트래픽이 최소로 유지되며, 각 속성을 독립적으로 deserialize하면 CPU 사용이 최소로 유지됩니다. 큰 세션 속성을 별도의 클러스터 캐시 엔트리로 분리시키면 Coherence*Web은 실제로 속성을 액세스하거나 업데이트할 경우 애플리케이션만 이러한 속성에 대한 비용을 지불하도록 합니다. 또한 Coherence*Web은 Coherence의 데이터 관리 기능을 사용하므로 근접 캐싱, NIO 버퍼 캐싱 및 디스크 기반 오버플로와 같은 세션 속성 관리에 기본적인 모든 기능을 사용할 수 있습니다.

Figure 3
그림 3. 세션 크기에 따른 성능. 각 세션은 10문자로 된 문자열 10개와 10,000문자로 된 문자열 0개에서 4개로 구성되어 있습니다. 각 HTTP 요청은 하나의 작은 속성과 하나의 큰 속성을 읽고(세션에 무언가가 있는 경우) 요청 중 50%는 이러한 속성을 업데이트합니다. 테스트는 두 개의 서버 클러스터에서 수행됩니다. Traditional 및 Monolithic 모델 간 성능이 유사하다는 점에 유의하십시오. 즉, serializing-deserializing 문자열은 최소한의 CPU 리소스를 사용하므로 실제로 사용되는 속성만 deserialize해도 성능에 별 도움이 안됩니다. Split 모델에서 성능 향상은 세션 크기가 1메가바이트(100개의 큰 문자열)에 이를 때까지 37:1 이상으로 증가됩니다. 클러스터 환경에서 필수 데이터만 액세스하는 애플리케이션 요청이 확장성과 성능을 향상시킬 수 있다는 것은 사실입니다. 이것이 바로 BEA가 세션을 적당한 크기로 유지하도록 하는 하나의 이유이기도 합니다.

또 다른 최적화는 세션 속성 클래스에 임시 데이터 멤버를 사용하는 것입니다. Java serialization 루틴은 임시 필드를 무시하기 때문에 세션 속성을 클러스터링할 것인지 아니면 단일 클러스터 멤버로 고립시킬 것인지를 아주 간편하게 제어할 수 있는 방법을 제공합니다. 이 방법은 데이터를 다른 데이터 소스로부터 "지연 로드"(및 서버 장애 조치 프로세스일 경우 재계산)할 수 있는 상황에서 유용하며, 또한 절대적 신뢰성이 중요하지 않은 시나리오일 경우에도 유용합니다. 세션 상태의 일부가 손실되어도 애플리케이션이 사용자에게 전혀(또는 아주 최소한) 영향을 미치지 않고 지탱할 수 있다면 성능 이점을 고려해 볼만 합니다. 이러한 맥락에서 볼 때, 고급 애플리케이션에서 세션 손실을 세션 시간 제한으로 처리하여 사용자에게 애플리케이션에 다시 로그인하도록 요구하는 것은 이상하지 않습니다(그러면 사용자의 예상을 애플리케이션 세션의 상태로 올바르게 설정할 수 있는 암시적인 이점이 있습니다).

고정 로드 밸런싱은 세션 상태가 클러스터에 걸쳐 전체적으로 보이지 않기 때문에 WebLogic Server에서 중요한 역할을 합니다. 고급 클러스터의 경우 일반적으로 사용자 요청은 일련의 비상태유지 로드 밸런서를 통해 애플리케이션 층에 들어가며, WebLogic Server 플러그인을 사용하는 Microsoft IIS 또는 Apache HTTP Server같은 일련의 고정 로드 밸런서에 이러한 요청을 다소 무작위로 재배포합니다. 이러한 고정 로드 밸런서는 WebLogic Server 인스턴스가 요청을(세션 쿠키에서 지정한 서버 ID에 기반하여) 처리할 것인지 결정하기 위해 HTTP 헤더를 구문 분석하는 강도 높은 계산 작업을 담당합니다. 어떠한 이유에서든 요청을 잘못 라우팅할 경우 세션 무결성은 상실됩니다. 예를 들어, 일부 로드 밸런서가 POST 데이터를 대량(예: 64KB 이상)으로 사용한 요청에서 HTTP 헤더를 구문 분석하지 못할 경우 이러한 요청은 적절한 WebLogic Server 인스턴스로 라우팅되지 못합니다. 라우팅이 실패하는 다른 이유로는 세션 쿠키에 손상되거나 잘못된 서버 ID가 있는 경우입니다. 이러한 문제의 대부분은 가능할 때마다 애플리케이션에 무정지 기능을 설계하거나(예를 들어, 모든 커다란 POST 요청이 세션 상태를 액세스하거나 변경하지 못하도록 함) 로드 밸런서를 제대로 선택하면 처리될 수 있습니다.

고정 로드 밸런싱이 Coherence*Web의 성능에 도움이 되긴 하지만 필수적이지는 않습니다. Coherence*Web은 Coherence 데이터 관리 플랫폼에 내장되어 있기 때문에 모든 세션 데이터는 클러스터에 걸쳐 전체적으로 볼 수 있습니다. 일반적인 Coherence*Web 배포는 세션 데이터를 근접한 캐시 토폴로지에 배치하고 파티션된 캐시를 사용하여 확장 가능한 무정지 방식으로 대량의 데이터를 관리하며, 각 애플리케이션 서버 JVM의 로컬 캐시를 결합하여 일반적으로 사용되는 세션 상태에 대한 인스턴트 액세스를 제공합니다. Coherence*Web을 사용할 때 고정 로드 밸런서가 필요하지는 않지만 이를 사용할 경우 두 가지 주된 이점을 얻을 수 있습니다. 근접 캐시 기술을 사용하므로, 로컬 캐시를 사용할 경우 deserialization 및 세션 속성의 네트워크 전송 비용이 발생되지 않기 때문에 사용자 요청이 지속적으로 동일한 서버로 라우팅되면 경우 세션 속성에 대한 읽기 액세스가 인스턴스화됩니다. 또한 고정 로드 밸런싱을 통해 Coherence는 로컬로 동시성을 관리할 수 있어서 사용자 요청이 또 다른 서버로 재밸런싱되는 경우에만 세션 잠금을 전송합니다.

결론

클러스터링은 애플리케이션의 확장성과 가용성을 촉진시킬 수 있습니다. WebLogic Server 및 Coherence*Web과 같은 클러스터링 솔루션으로 개발자는 많은 문제를 해결할 수 있지만, 성공적인 개발자라면 기본적인 기술의 한계와 이러한 한계를 관리하는 방법을 알고 있어야 합니다. 플랫폼에서 제공하는 기능과 사용자들이 필요로 하는 기능을 이해한다면 개발자는 이 둘 간의 격차를 없앨 수 있을 것입니다.

추가 자료

Jon Purdy는 Tangosol의 공동 창업자로, 이 회사는 메모리 내 캐싱을 제공하고 클러스터 J2EE 애플리케이션에 고도로 확장 가능한 성능과 신뢰성을 제공하는 데이터 관리 소프트웨어 솔루션을 제공합니다.

3901 view

4.0 stars