본 장에서는 Stateless Session Bean과 Stateful Session Bean에 대해서 자세히 설명한다.
Stateless Session Bean은 사실상 “제4장 EJB의 공통 특성”에서 설명한 내용 외에는 특별한 것이 없다. 따라서 본 장에서는 주로 Stateful Session Bean들에 대해 설명한다.
본 절에서는 Session Bean의 Thread Ticket Pool(이하 TTP)에 대해 설명한다.
Stateless Session Bean은 클라이언트의 요청에 관련된 상태 정보를 가지고 있지 않기 때문에 모든 클라이언트가 커넥션을 공유하여 Connection Pool은 설정할 필요가 없다. 그러나 상태 정보를 가지고 있지 않은 이유로 Bean Instance를 재활용할 수 있기 때문에 Bean Pool을 설정할 수 있다.
다음은 Stateless Session Bean의 TTP과 Bean Pool의 관계를 나타낸 그림이다.
create 시점에 클라이언트와 연결을 맺은 커넥션으로 요청하면 TTP에서 Thread Ticket(이하 TT)을 발급 받는다. TT를 발급받지 못하면 “제4장 EJB의 공통 특성”에서 설명했듯이 TT를 발급받을 때까지 기다린다. 기다리는 시간이 10분을 초과하면 RemoteException이 발생하여 요청을 수행하지 못한다.
TT를 발급받은 클라이언트의 요청을 수행하기 위해 실제 Bean instance를 Bean Pool로부터 할당받아 커넥션과 연결을 맺는다.
Stateless Session Bean Instance가 처리를 끝냈을 때 TT는 TTP로 반환되고 Bean Instance는 Bean Pool로 반환되어 다음 클라이언트 요청을 기다리게 된다. 즉, 매 요청마다 TT와 Bean Instance를 새로 할당받고 요청이 끝나면 반환된다.
Bean Pool 개수의 의미가 TTP 개수의 의미와 다른 점은 동시 수행될 수 있는 로컬 클라이언트와 리모트 클라인언트의 요청의 개수와 관련된다는 점이다. 즉, Remote call은 TTP에서 TT를 할당받은 후 Bean Pool에서 Instance를 할당받을 수 있고, Local call은 클라이언트 스레드에서 바로 수행되므로 TT를 발급받을 필요가 없고 Bean Pool로부터 Instance만 할당받는다.
따라서 jeus-ejb-dd.xml의 <thread-max>의 값을 작게 설정하면 설정값 이상의 Remote call은 받을 수 없고, Bean Pool의 최댓값은 무한대이기 때문에 Locall call은 처리할 수 있다.
EJB 2.1 이후부터 Stateless Session Bean은 웹 서비스 형태로 Export될 수 있다.
이에 대한 자세한 정보는 "JEUS Web Service 안내서"를 참고한다.
본 절에서는 다음의 Stateful Session Bean의 추가적인 설정들의 개념에 대해 설명한다.
Object Management 설정 : Bean Instance Pool과 Connection Pool
Bean Instance Pooling 옵션
Session Manager를 통한 상태 유지 메커니즘
Concurrent Access 설정
Pooling을 통한 성능 향상이 존재하지 않아 JEUS v6.0 Fix#8부터는 Connection Pool 설정이 적용되지 않고, JEUS v6.0 Fix#9부터는 Stateful에 대한 Bean Pool 설정이 적용되지 않는다.
Stateful Session Bean은 클라이언트의 요청에 관련된 상태 정보를 지속적으로 유지하고 있어야 하기 때문에 Stateless Session Bean과는 달리 Connection Pool을 설정하고 활용해야 한다. 이는 Entity Bean에도 동일하게 적용된다. 자세한 내용은 “제8장 Entity Bean”을 참고한다.
다음은 Stateful Session Bean의 Connection Pool과 TTP, Bean Pool의 관계를 나타낸 그림이다.
클라이언트가 Stateful Session Bean을 생성하면 새로운 Bean Instance(SF Instance)가 만들어지고 Connection Pool에서 커넥션을 꺼내 2개가 연결된다. Bean Instance와 연결된 커넥션을 클라이언트에게 넘겨주면 이 커넥션은 현 클라이언트에게 할당되고 다른 클라이언트와 공유하지 않는다. 따라서 클라이언트가 제거하지 않는 한 다시 Connection Pool로 반환되지 않는다. Connection Pool로 반환될 때에는 연결 되었던 Bean Instance와의 연결을 끊는다.
해당 커넥션으로 요청을 하게 되면 TTP에서 TT를 받는다. TT를 받은 요청만 처리한다. Stateless Session Bean과 달리 매 요청마다 새로운 Bean Instance가 Bean Pool로부터 할당되는 것이 아니라 해당 클라이언트를 위한 Bean Instance가 고정되어 있다.
앞에서 설명했듯이 이 Bean Instance는 클라이언트가 Bean을 제거했을 때 커넥션이 Connection Pool로 반환되면서 커넥션과의 연결을 잃으면서 삭제된다. 기본적으로 Stateful Session Bean은 상태가 있기 때문에 Bean Instance를 재활용하는 Bean Pool을 사용하지 않는다.
EJB 표준에 따르면, 실제 Stateful Session Bean의 Instance는 다른 클라이언트 Session에서 재사용할 수 없다. 그러나 Bean이 제거될 때 초기화가 제대로 이루어진다면 Bean Instance를 재사용할 수도 있다. JEUS는 Bean Instance를 매번 생성하지 않는 점에서 성능 개선과 자원 낭비를 줄이기 위해 이를 재사용하는 방법을 제공한다.
Bean Pool을 사용하면 Bean이 제거될 때 해당 Bean Instance가 Bean Pool로 반환된다. Bean의 제거는 클라이언트가 명시적으로 remove를 부르거나 오랫동안 요청이 없어 타임아웃이 된 경우에 발생한다. 이때 PRE DESTROY callback이 불리게 되는데 여기서 Bean의 초기화를 잘 구현한 경우만 사용하도록 권장한다.
Stateful Session Bean을 Pooling Bean으로 전환하려면 간단히 jeus-ejb-dd.xml의 <pooling-bean> 값을 true로 설정한다.
[예 7.1] Bean Pool 설정 : <<jeus-ejb-dd.xml>>
<jeus-ejb-dd> . . . <beanlist> <jeus-bean> <ejb-name>teller</ejb-name> <export-name>TELLEREJB</export-name> <local-export-name>LOCALTELLEREJB</local-export-name> <export-port>7654</export-port> <export-iiop>true</export-iiop> <single-vm-only>true</single-vm-only> <pooling-bean>true</pooling-bean> . . . </jeus-bean> . . . </beanlist> . . . </jeus-ejb-dd>
이름에서 암시하듯이 Stateful Session Bean은 상태를 지니고 다닌다. 이 상태는 클라이언트 세션동안 반드시 유지되어야 한다. 즉, 같은 클라이언트로부터 오는 모든 다른 요청에도 상태는 지속되어야 한다. 이것이 Stateful Session Bean의 기본 특성이다.
일반적으로 런타임할 때의 상태는 Instance 변수로 저장된다. 그러나 위에서 설명한 것과 마찬가지로 Stateful Session Bean이 passivate 상태로 진입하면 시스템 자원을 보존하기 위하여 운영환경에서 이 Instance 변수들을 제거해야 한다. 그러나 Bean이 다시 재활성화되면 상태를 복구하기 위해서 어딘가에서 데이터를 가져와야 한다. 이게 2차 저장소가 필요한 이유이다.
JEUS에 포함된 분산식 Session Manager를 2차 저장소로 사용한다. 기본적으로는 passivate될 때 클러스터링으로 구성된 Stateful Session Bean의 경우는 Failover를 위해 트랜잭션의 commit이 성공적으로 수행될 때 Bean의 정보가 Session Manager로 전달된다. Session Manager는 JEUSMain.xml에서 설정된다. 클러스터링 환경이 아닌 경우에는 아무 설정을 하지 않아도 동작하지만 클러스터링 환경에서는 어떤 EJB 엔진이 어떤 EJB 엔진을 백업해줄지 Session Manager가 알아야 하기 때문에 현재 떠 있는 EJB 엔진을 모두 JEUSMain.xml에 설정해야 한다.
세션 데이터 저장 메커니즘인 Session Manager는 JEUSMain.xml에서 설정한다. 설정하지 않아도 다음과 같은 설정으로 동작한다.
[예 7.2] 세션 데이터 유지 메커니즘 설정 : <<JEUSMain.xml>>
<?xml version="1.0"?> <jeus-system xmlns="http://www.tmaxsoft.com/xml/ns/jeus"> <node> ... <session-router-config> <thread-pool> <min>2</min> <max>30</max> <period>3600000</period> </thread-pool> <use-nio>true</use-nio> <connect-timeout>5000</connect-timeout> <read-timeout>20000</read-timeout> <backup-trigger>1</backup-trigger> <check-to>30000</check-to> <default-file-db> <startup-clear-to>86400000</startup-clear-to> <passivation-to>300000</passivation-to> <min-hole>1000</min-hole> <packing-rate>0.5</packing-rate> </default-file-db> <session-router> <engine-name>johan_ejb_engine1</engine-name> </session-router> ... </session-router-config> ... </node>
다음은 설정 태그에 대한 설명이다.
태그 | 설명 |
---|---|
<backup-trigger>,<check-to> | Session Manager가 자신의 백업으로 Bean 정보를 보내는 것과 관계된 element이다. 백업으로 보낼 Bean의 정보가 <backup-trigger>의 개수보다 많거나, 이전의 백업으로 정보를 보낸 시간으로부터 <check-to>에 설정한 ms가 지나면 백업 세션 서버로 Bean의 정보를 보내게 된다. <check-level>은 백업으로 보낼 Bean의 정보를 필터하는 역할을 하는데 사용자가 임의로 바꾼다고 해도 내부 값을 사용하기 때문에 신경쓰지 않아도 된다. |
<default-file-db> | Bean의 정보를 passivate하는 것과 관계된 element이다. EJB 컨테이너는 EJBMain.xml의 <resolution>에 설정된 ms마다 passivate되거나 disconnect할 Bean이 있는지 체크한다. 이때 마지막으로 Bean에 접근한 시간이 <passivation-to> ms을 초과한 Bean들은 passivation 대상이 된다. <default-file-db>가 설정되었을 경우 passivation되어야 할 대상 Bean들은 JEUS_HOME/logs/sessiondb/<node-name>_<engine>_<engine-name>_1.fdb 파일에 저장되고 이 파일은 <min-hole>과 <packing-rate> 값에 의해 정리된다. |
<session-router> | 세션 클러스터링에 참여하는 컨테이너에 기동될 세션 서버에 대한 각종 속성을 설정한다. 각 EJB 엔진마다 하나의 태그가 생기고 하위에는 엔진 이름과 백업 Session Manager를 선택하는 정보가 있다. 노드 클러스터링만 제대로 하면 설정하지 않아도 백업 서버는 내부적으로 선택된다. |
Stateful Session Bean을 클러스터링 환경에서 사용하고 싶다면 클러스터링에 포함할 모든 EJB 엔진 이름을 Session Manager에 알려주어야 한다. 자세한 설정은 “6.3.3. Stateful Session Bean의 클러스터링 설정”을 참고한다.
본 절에서는 EJB에 초점을 맞추어 굵은 태그만 간락히 설명하고, 각 태그에 대한 자세한 내용은 “JEUS Server 안내서”의 “제10장 세션 서버” 설정을 참고한다.
Stateful Session Bean의 경우 기본적으로 하나의 Bean에 대한 동시접근은 허용되지 않는다. 특정 세션을 사용 중인 클라이언트가 있고, 같은 시점에 다른 클라이언트가 해당 세션에 접근하려고 한다면 ConcurrentAccessException이 발생한다.
JEUS에서는 세션에 대한 접근을 대기할 수 있도록 하는 옵션을 제공한다. 즉, 새로운 클라이언트가 ConcurrentAccessException을 받지 않고 해당 세션에 대한 사용이 끝날 때까지 기다리는 것이 가능하다. 이때의 접근 대기는 무한대기이므로 사용에 주의하도록 한다.
[예 7.3] Session 접근 대기 설정 : <<jeus-ejb-dd.xml>>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<jeus-ejb-dd xmlns="http://www.tmaxsoft.com/xml/ns/jeus">
<beanlist>
<jeus-bean>
<ejb-name>counter2</ejb-name>
<export-name>counter2</export-name>
<allow-concurrent-access>true</allow-concurrent-access>
...
</jeus-bean>
</beanlist>
</jeus-ejb-dd>
본 절에서는 Stateless Session Bean과 Stateful Session Bean의 공통 설정 항목에 대해서 설명한다. 이 모든 항목들은 JEUS EJB 모듈 DD의 <jeus-bean> 태그 아래에 설정된다.
Session Bean은 다음과 같이 제공되는 설정을 통해 Bean Pool을 이용하여 매번 Instance를 생성하는 부하를 줄일 수 있다.
다음은 Object Management 관련 설정한 XML 예제이다.
[예 7.4] Object Management 설정 : <<jeus-ejb-dd.xml>>
<jeus-ejb-dd> . . . <beanlist> <jeus-bean> . . . <object-management> <!--Fix#9과 이후 버전에서는 Stateful Session Bean을 위한 bean-pool 설정이 적용되지 않는다.--> <bean-pool> <pool-min>10</pool-min> <pool-max>200</pool-max> </bean-pool> <!--Fix#8과 이후 버전에는 connect-pool 설정이 적용되지 않는다.--> <connect-pool> <pool-min>10</pool-min> <pool-max>200</pool-max> </connect-pool> <passivation-timeout>10000</passivation-timeout> <disconnect-timeout>1800000</disconnect-timeout> </object-management> <jeus-bean> . . . <beanlist> . . . <jeus-ejb-dd>
다음은 설정 태그에 대한 설명이다.
EJB Bean Instance Pool의 작동 방식을 결정한다.
하위 태그들은 다음과 같다.
태그 | 설명 |
---|---|
<pool-min> | Pool을 초기화할 때 생성해두는 초기 Bean Instance 개수이다. (기본값: 0) |
<pool-max> | Instance의 사용이 완료된 후 Pool에 저장 가능한 최대 Bean Instance 개수이다. (기본값: 100) |
클라이언트와 Bean Instance를 연결하는 커넥션을 몇 개까지 유지할지를 설정한다.
하위 태그들은 다음과 같다.
태그 | 설명 |
---|---|
<pool-min> | Pool을 초기화할 때 생성해두는 초기 커넥션 개수이다. (기본값: 0) |
<pool-max> | 커넥션 사용이 완료된 후 Pool에 저장 가능한 최대 커넥션 개수이다. (기본값: 100) |
지정된 시간 동안 클라이언트의 요청을 받지 않은 Stateful Session Bean을 passivate할 때 사용된다. 그러므로 여기에 설정된 시간을 초과하는 동안 클라이언트의 요청이 없으면 그 Bean은 passivation의 대상이 된다. 어떤 Bean이 passivation 대상이 되는지를 검사하는 주기는 EJBMain.xml에 설정한 <resolution>을 따른다.
Passivation이 실행되면 메모리에서 해당하는 Bean Instance가 제거되고 Instance의 상태는 파일에 저장된다. 내부적으로 분산 세션 서버를 사용한다.
이 설정은 여러 곳에서 설정이 가능하고 우선 순위는 다음과 같다.
특정 Session Bean에만 적용 : jeus-ejb-dd.xml의 <passivation-timeout>
모든 Stateful Session Bean에 적용 : 시스템 프로퍼티 jeus.ejb.stateful.passivate
모든 EJB Bean에(Entity Bean과 Session Bean 모두) 적용 : 시스템 프로퍼티 jeus.ejb.all.passivate
모든 EJB Bean에(Entity Bean과 Session Bean 모두) 적용 : JEUSMain.xml의 <node>/<session-router-config>/<session-router>/<file-db>/<passivation-to>
위의 모든 설정이 없으면 기본값으로 설정된다. (기본값: 300000ms(5분), 단위: ms)
지정된 시간 동안 클라이언트의 요청을 받지 못하면 클라이언트와 Stateful Session Bean Instance 사이를 연결하던 커넥션을 끊을 때 사용된다. 그렇게 되면 커넥션은 각각의 클라이언트 및 Instance와 맺고 있던 연결을 끊고 Connection Pool로 반환된다. 따라서 클라이언트는 이 커넥션으로 더 이상 요청을 할 수 없고, 사용 중이던 Bean Instance는 삭제되거나 BeanPool을 사용 중이면 Bean Pool로 반환된다.
이 설정은 여러 곳에서 설정 가능하고 우선순위는 다음과 같다.
특정 Session Bean에만 적용 : jeus-ejb-dd.xml의 <disconnect-timeout>
모든 Stateful Session Bean에 적용 : 시스템 프로퍼티 jeus.ejb.stateful.disconnect
모든 EJB Bean에(Entity Bean과 Session Bean 모두) 적용 : 시스템 프로퍼티 jeus.ejb.all.disconnect
위의 모든 설정이 없으면 시스템 프로퍼티 jeus.ejb.all.disconnect의 기본값으로 설정된다.
(기본값: 3600000(1시간), 단위: ms)
<passivation-timeout>과 <disconnect-timeout>에 사용되는 시간은 Bean Instance에 액세스했던 마지막 시점부터 측정된다. 그러므로 <disconnect-timeout>을 <passivation-timeout>보다 길게 설정해야 한다. 또한 <passivation-timeout>은 EJBMain.xml에 설정되는 <resolution>보다는 커야한다.
타임아웃 값을 길게 설정하면 오랜 시간(대략 십여 분 이상 또는 타임아웃이 중지된 경우) 동안 메모리 내의 많은 Instance가 활성화된 상태로 머물러 있다. 그러므로 시스템 자원 낭비를 초래한다. 너무 짧은 타임아웃 값은(수 초) passivation, activation등의 작업이 너무 자주 발생하므로 성능을 저하시킬 수 있고 disconnect 작업으로 인해 세션 유실 가능성이 있다.