제8장 DB Connection Pool과 JDBC

내용 목차

8.1. 개요
8.2. JDBC Connection Pool
8.2.1. Connection Pooling
8.2.2. 데이터소스
8.2.3. 클러스터 데이터소스 및 장애복구 기능
8.3. JDBC 데이터소스 설정
8.3.1. 데이터소스 관리
8.3.2. 기본 설정
8.3.3. Custom DB 속성 추가
8.3.4. Connection Pool 설정
8.3.5. 클러스터 데이터소스 설정
8.4. DB Connection Pool의 제어 및 모니터링
8.4.1. DB Connection Pool의 제어
8.4.2. DB Connection Pool의 모니터링
8.5. JEUS JDBC 프로그래밍
8.5.1. 간단한 JDBC 프로그래밍
8.5.2. 트랜잭션 프로그래밍 규칙
8.5.3. JDBC 드라이버의 커넥션 인스턴스를 얻고 싶을 때
8.5.4. Standalone 클라이언트에서의 Connection Pool
8.6. 글로벌 트랜잭션(XA)과 커넥션 공유
8.7. 복수 사용자에 대한 Connection Pooling 서비스 지원

본 장에서는 JEUS에서 제공하는 Connection Pool 및 부가 기능들에 대해 설명하고 그 사용법에 대해 설명한다.

8.1. 개요

웹 애플리케이션은 정보 저장이 필요할 때 주로 데이터베이스(Database, 이하 DB)를 이용한다. 이를 위해 JEUS와 같은 Web Application Server(WAS)는 애플리케이션이 DB로 접근할 수 있는 통일된 방법을 제공해야 한다. 이러한 통일된 DB 접근을 위해 만든 표준이 바로 JDBC(Java Database Connectivity) 표준이다. JDBC 표준은 애플리케이션이 DB 커넥션을 사용하는 방법에 대해 기술하고 SQL 작업을 하기 위한 API를 제공하고 있다.

참고

표준에 관한 자세한 내용은 Sun JDBC 페이지(http://www.oracle.com/technetwork/java/javase/tech/index-jsp-136101.html)를 참고한다.

JEUS에서는 애플리케이션의 원활한 DB 사용을 위해 Connection Pool과 여러 부가 기능들을 제공한다.

8.2. JDBC Connection Pool

JDBC는 Java 애플리케이션이 DB에 접속하기 위한 Java 표준 API이다. 현재 JEUS는 JDBC 3.0을 지원하고 있다. JDBC 표준에 관한 좀 더 자세한 사항은 관련 웹 페이지 또는 서적을 참고한다.

JEUS에서는 응용 프로그램의 효율적인 DB 사용을 위한 Connection Pool을 제공한다. 사용자는 그러한 기능들을 사용하기 위하여 이하 소개되는 내용들을 숙지하고 알맞은 설정을 해야 한다.

Connection Pool 사용의 이점들은 다음의 2가지로 요약할 수 있다.

  • 보다 높은 성능

    DB 커넥션 생성은 처리 과정이 느리다. Connection Pool 안에서의 모든 실제 커넥션들은 미리 만들어져 애플리케이션의 요청 처리를 위한 준비가 되어 있다. 커넥션을 더 이상 사용하지 않을 때에는 그것을 Pool에 반환시켜서 커넥션 중단의 오버헤드를 감소시킬 수 있다.

  • 연결 관리

    동시 커넥션들의 수를 제어할 수 있다. 동시 커넥션들의 최대 수를 구성함으로써 DB의 동시 커넥션을 제한하는 작업을 효율적으로 할 수 있다.

JEUS의 Connection Pool은 서버 기동 시점에 생성되지 않고 실제 서비스를 호출할 때 생성된다. 만약 Connection Pool을 생성하고 싶다면 jeusadmin 또는 WebAdmin을 통해서 생성할 수 있다.

Connection Pool 생성 방법에 관한 좀 더 자세한 사항은 JEUS Reference Book”의 “4.2.7. DB Connection Pool 관련 명령어”을 참고한다.

JDBC 드라이버

JEUS는 JDBC 인증을 받은 드라이버들을 지원한다. 인증된 드라이버의 타입이나 그 종류는 Sun의 'Types of JDBC technology drivers' (http://www.oracle.com/technetwork/java/javase/jdbc/index.html) 문서에서 찾을 수 있다. JEUS의 JDBC 환경 구성은 어떤 벤더의 DB를 사용하느냐에 따라 환경 구성이 서로 다를 수 있다. 이는 각 드라이버마다 각자 요구하는 속성이 다르기 때문이므로 각 벤더의 JDBC 드라이버 매뉴얼을 참고해야 한다.

8.2.1. Connection Pooling

Connection Pooling은 DB 커넥션을 위한 하나의 프레임워크(Framework)이다. Connection Pool이 시작될 때 특정한 수의 물리적 커넥션을 만들며 이는 애플리케이션 실행 중에 커넥션 생성을 위한 오버헤드(Overhead)를 줄여준다.

다음 그림은 JEUS JDBC Connection Pooling의 전체적인 구조를 보여준다.

[그림 8.1] JEUS의 Connection Pooling

JEUS의 Connection Pooling

8.2.2. 데이터소스

데이터소스는 애플리케이션과 Connection Pool 사이의 인터페이스이다. 애플리케이션이 사용하는 데이터소스 객체는 DB 커넥션들의 Factory로 볼 수 있으며 java.sql.DriverManager 보다 많은 이점을 제공한다.

데이터소스들은 다음의 4가지의 타입을 갖는다.

  • 기본 데이터소스

  • Connection Pool 데이터소스

  • XA 데이터소스

  • 로컬 XA 데이터소스

기본 데이터소스

javax.sql.DataSource 타입을 의미한다. JEUS는 이 타입에 대해 Connection Pool을 제공하지 않으므로 Connection Pool 데이터소스를 사용하기 바란다.

Connection Pool 데이터소스

javax.sql.ConnectionPoolDataSource 타입을 의미한다. JEUS는 이 타입에 대해 Connection Pool을 제공한다.

주로 다음과 같은 상황에서 사용할 수 있다.

  • XA를 사용할 필요없이 간단하게 DB에 접근하기 위한 애플리케이션을 작성하는 경우

  • auto-commit을 false로 설정하고 직접 로컬 트랜잭션을 컨트롤하는 경우

    import java.sql.Connection;
    
    Connection conn = datasource.getConnection();
    conn.setAutoCommit(false);
    
    .... DB 접근 코드 ... // JDBC 드라이버 내부적으로 로컬 트랜잭션 진행
    
    conn.commit();

XA 데이터소스

javax.sql.XADataSource 타입을 의미한다. JEUS는 이 타입에 대해 Connection Pool을 제공한다.

글로벌 트랜잭션(XA)에 이용되는 커넥션을 관리하며, 주로 다음과 같은 상황에서 사용할 수 있다.

  • 서블릿/EJB와 같은 Java EE 컴포넌트 로직이 2개 이상의 DB로 접근해야 하는 경우

  • Java EE 컴포넌트에서 하나의 DB로 접근하더라도 관련 로직들이 일련의 동작으로 묶여야 하는 경우

이때 XA 처리 성능을 높이기 위해서 로컬 트랜잭션 최적화를 선택할 수 있다. 이에 관한 자세한 사항은 "로컬 XA 데이터소스"의 설명을 참고한다.

로컬 XA 데이터소스

Connection Pool 데이터소스를 통해서 얻은 커넥션을 XA에 참여하도록 에뮬레이션 해주는 데이터소스이다. JEUS는 이 타입에 대해 Connection Pool을 제공한다. 이 타입은 주로 다음과 같은 상황에서 사용할 수 있다.

  • DB가 XA를 지원하지 않거나 JDBC 드라이버가 javax.sql.XADataSource 구현체를 지원하지 않는 경우

  • 애플리케이션에서 XA(글로벌 트랜잭션)가 필요하지만 성능 문제로 XA 데이터소스를 사용하고 싶지 않은 경우(즉, 로컬 트랜잭션 최적화가 필요한 경우)

최근에는 XA를 지원하지 않는 DB 또는 JDBC 드라이버는 거의 없다고 볼 수 있기 때문에 주로 로컬 트랜잭션 최적화를 위해서 이 데이터소스를 사용하게 될 것이다. 그러나 반드시 고려해야 할 제약 사항이 있다. 바로 하나의 XA에는 최대 하나의 로컬 XA 데이터소스만 참여할 수 있다는 것이다.

따라서 로컬 XA 데이터소스는 되도록 다음과 같은 상황에서 사용하기 바란다.

  • 서블릿/EJB와 같은 Java EE 컴포넌트 로직이 하나의 DB만 사용하기 때문에 굳이 XA 데이터소스를 사용할 필요가 없을 때

  • Java EE 컴포넌트에서 여러 개의 DB를 사용하더라도 그 중 하나를 로컬 트랜잭션으로 처리해서 XA 성능을 높이고 싶을 때

여러 개의 DB를 사용하는 상황에서 주의할 점은 로컬 XA 데이터소스는 실제로 2PC(2 Phase Commit)를 지원하는 것이 아니므로 트랜잭션 Recovery가 제대로 되지 않을 수 있다.

다른 WAS 벤더(주로 WebLogic)와는 달리 다음과 같이 Connection Pool 데이터소스에 LocalXADataSource 타입을 지정하는 방식이다. 차기 버전에서는 사용자의 혼란을 줄이기 위해서 다른 WAS 벤더의 설정과 유사하게 변경될 예정이다.

<data-source>
    <database>
        <vendor>tibero</vendor>
        <export-name>datasource1</export-name>
        <data-source-class-name>
        com.tmax.tibero.jdbc.ext.TbConnectionPoolDataSource
        </data-source-class-name>
        <data-source-type>LocalXADataSource</data-source-type>
        .....
    </database>
    ....
</data-source>

참고

로컬 XA 데이터소스에서 얻은 커넥션이 XA에 참여할 때는 auto-commit을 끄고 사용하는 로컬 트랜잭션이 되기 때문에 DB 입장에서는 JEUS 트랜잭션 매니저가 관리하는 XA 트랜잭션과는 서로 다른 트랜잭션이다. 대신 JEUS에서는 로컬 트랜잭션이 그 XA 트랜잭션에 참여할 수 있도록 에뮬레이션해서 애플리케이션 입장에서는 같은 트랜잭션으로 처리해주는 것이다.

지금까지 JEUS 설정에서 제공하는 다양한 데이터소스 형식을 살펴보았다. 데이터소스에는 다양한 종류가 있고 각각 필요한 상황이 있으므로, 애플리케이션에서 성능과 비즈니스 로직을 고려해서 그에 맞는 타입을 사용해야 한다.

8.2.3. 클러스터 데이터소스 및 장애복구 기능

JEUS 레벨에서 RAC 인스턴스 간의 Failover & Failback을 제공하기 위해서 클러스터 데이터소스(Cluster Datasource)를 제공한다. RAC(Real Application Cluster)는 Oracle에서 제공하는 DB 클러스터링 기능이다. RAC에 관한 자세한 내용은 Oracle의 문서를 참고한다.

클러스터 데이터소스는 근본적으로는 하나의 JNDI 이름을 가진 데이터소스 인스턴스이다. 이 인스턴스는 애플리케이션의 요청을 우선 주 RAC 인스턴스로 전달시켜주는 역할을 한다. 만약 주 인스턴스가 종료(Down)되었을 경우 백업 RAC 인스턴스가 선택되어서 요청 사항을 처리하게 된다. 애플리케이션에서는 단지 하나의 데이터소스만 보게 되므로 장애복구가 투명하게 처리된다.

[그림 8.2] RAC에서 클러스터 데이터소스 Failover 기능

RAC에서 클러스터 데이터소스 Failover 기능


그리고 Oracle JDBC 드라이버 레벨에서 제공하는 CTF(Connect Time Failover)보다는 JEUS의 클러스터 데이터소스를 사용하길 권장한다. Oracle CTF의 경우에는 커넥션 별로 Failover를 하기 때문에 데이터소스 전체가 문제가 생겼을 경우 Pool에 있는 모든 커넥션마다 Failover를 해야 하기 때문에 성능이 떨어진다. 그러나 JEUS 클러스터 데이터소스에서는 데이터소스에 문제가 생긴 것을 감지하면 데이터소스 단위로 Failover를 하므로 더 효율적이며, 자동으로 Failback을 하는 기능도 제공한다.

참고

1. JEUS의 클러스터 데이터소스를 사용하는 방식과 Oracle JDBC 드라이버에 RAC 속성을 설정하는 방식은 서로 동작이 다르다는 점에 주의해야 한다. 전자는 Failover를 JEUS가 처리하는 것이기 때문에 각 RAC 인스턴스의 FAIL 여부를 감지하기 위해서 <check-query>와 <check-query-period>를 설정해야 한다. 그러나 후자는 드라이버가 Failover를 담당하게 되므로 <check-query>가 필수적인 것은 아니다.

2. JEUS v6.0 Fix#3부터는 기본적으로 Failback을 지원하므로 이를 위한 설정이 반드시 필요하다. 클러스터 데이터소스 설정에 관한 내용은 “8.3.5. 클러스터 데이터소스 설정”을 참고한다.

8.3. JDBC 데이터소스 설정

JEUS에서 JDBC 드라이버 구성을 시도할 때 JEUS_HOME\lib\datasource\ 디렉터리 내에 JDBC 드라이버 클래스 라이브러리가 존재하는지 먼저 확인한 후 다음 절과 같이 JEUSMain.xml을 구성하거나, WebAdmin을 통해 설정을 해야 한다. 대부분의 경우 WebAdmin을 통해 이를 설정할 것을 권장한다.

8.3.1. 데이터소스 관리

JEUS v6.0 Fix#7에서 데이터소스를 노드 내의 특정 엔진 컨테이너에서만 사용할 수 있는 기능이 추가되었다. 이에 따라 데이터소스를 관리하는 방식이 변경되었다.

JEUS v6.0 Fix#6까지는 데이터소스를 설정하면 모든 엔진 컨테이너에서 사용할 수 있다. 하지만 같은 컨테이너 별로 Connection Pool 설정을 서로 다르게 설정하고 싶은 요구사항이 자주 발생하였는데, 기존의 구조에서는 지원할 수가 없었고, 이는 다른 벤더와 비교해 약점으로 지적되었다. 엔진 컨테이너마다 서로 다른 데이터소스를 사용하게 하는 게 한 가지 방법이지만 <export-name>이 서버에서 관리하는 키로 사용되어 다른 데이터소스는 다른 <export-name>을 정의해야 하고, 이는 lookup하는 이름이 달라지므로, 어떤 컨테이너에 디플로이하느냐에 따라 애플리케이션을 바꿔야 하는데 이는 좋지 않은 방법이다.

이에 대한 해결 방법으로 JEUS v6.0 Fix#7에서는 <data-source-target>을 추가하고, 그동안 암시적으로 <export-name>을 데이터소스 관리 키로 사용하던 방식에서 데이터소스 ID를 명시적으로 입력하는 방식으로 바뀌었다. 바꿔 말하면 이제 더 이상 <export-name>을 데이터소스를 구별하는 이름으로 사용하지 않기 때문에 서로 다른 데이터소스가 같은 <export-name>을 설정할 수 있다. 물론 같은 <export-name>을 사용하는 서로 다른 데이터소스들이 같은 엔진 컨테이너를 Target으로 지정한다면 해당 컨테이너에서 제대로 동작하지 않지만, 이런 충돌만 조심하면 문제 없이 동작한다. 위에서 문제라고 설명한 부분은 다음의 방법으로 해결할 수 있다.

예를 들어 애플리케이션에서 'DS1'이라는 이름으로 데이터소스를 lookup하는 로직을 짜고, 컨테이너 container1, container2에 각각 디플로이하는 경우를 생각해 보자.

이 경우에 container1에 알맞는 Connection Pool 설정값들로 만든 데이터소스를 'datasource1'이라는 ID로 만들고, container2에 알맞는 Connection Pool 설정값들로 만든 데이터소스를 'datasource2'라는 ID로 만들면 같은 <export-name>이지만 컨테이너별로 Connection Pool 구성을 다르게 해서 사용할 수가 있다. 물론 Target과 <export-name>은 상황에 맞게 잘 설정해야 한다.

8.3.2. 기본 설정

JEUSMain.xml에 데이터소스를 설정할 수 있다. javax.sql.DataSource의 속성들은 각 드라이버별로 다르기 때문에 사용하기 원하는 드라이버의 특성을 파악하고 그 특성에 맞게 설정을 해야 한다.

참고

각 태그들의 전체 리스트는 "JEUS_HOME\docs\reference\schema\index.htm"에 있는 XML Reference에서 찾을 수 있다. 또한 JDBC 구성 예제들은 “Appendix B. JDBC Datasource 구성 예제”를 참고한다.

다음은 Oracel 데이터소스로 설정한 예제로 <resource><data-source>...<database> 태그에 설정한다.

[예 8.1] JDBC 데이터소스 설정 : <<JEUSMain.xml>>

<jeus-system>
    . . .
    <resource>
        <data-source>
            <database>
                <vendor>oracle</vendor>
                <!-- JEUS 6 Fix#7에서 추가됨 -->
                <data-sourcce-id>datasource1</data-source-id>
                <export-name>jdbc/ds1</export-name>
                <!-- JEUS 6 Fix#7에서 추가됨 -->
                <data-source-target>johan_container1</data-source-target>
                <data-source-class-name>
                   oracle.jdbc.pool.OracleConnectionPoolDataSource
                </data-source-class-name>
                <data-source-type>ConnectionPoolDataSource</data-source-type>
                <database-name>ora9i</database-name>
                <description>
                   This is a sample for database setting.
                </description>
                <user>scott</user>
                <password>tiger</password>
                <port-number>1521</port-number>
                <server-name>192.168.1.2</server-name>
                <property>
                    <name>driverType</name>
                    <type>java.lang.String</type>
                    <value>thin</value>
                </property>
                <auto-commit>true</auto-commit>
                <action-on-connection-leak>Warning</<action-on-connection-leak>
                . . . <!-- See later sub-section -->
            </database>
            . . .
        </data-source>
        . . .
    </resource>
</jeus-system>

다음은 설정 태그에 대한 설명이다.

태그설명
<vendor>

DB 벤더의 이름을 설정한다. 현재 사용하고 있는 DB의 벤더 이름을 사용하고, 나열되지 않은 DBMS는 others로 설정하도록 한다.

  • oracle

  • mssql

  • db2

  • sybase

  • tibero

  • others

<data-source-id>

노드에서 관리하는 데이터소스 ID를 설정한다.

JEUS v6.0 Fix#7에 추가되었고, JEUS v6.0 Fix#0 ~ Fix#6 버전에서는 <export-name>이 데이터소스 ID 역할을 한다.

<export-name>

JNDI에 바인딩될 이름을 설정한다.

이 이름으로 데이터소스 객체가 bind된다. 데이터소스 ID가 설정되지 않은 경우 여기에 설정된 값을 내부적으로 ID로 사용한다. 하나의 엔진 컨테이너에 대하여 동일한 <export-name>을 가지는 서로 다른 데이터소스가 정의될 수 없음에 유의해야 한다.

<data-source-target>

데이터소스를 사용할 엔진 컨테이너 이름을 설정한다.

JEUS v6.0 Fix#7에서 추가되었다. 설정하지 않으면 노드에 속한 모든 엔진 컨테이너에서 사용할 수 있다.

<data-source-class-name>JDBC 드라이버별 데이터소스 클래스 이름을 설정한다.
<data-source-type>

사용하려는 데이터소스 타입에 맞게 선택한다.

  • DataSource

  • ConnectionPoolDataSource

  • XADataSource

  • LocalXADataSource

<data-source-name>데이터소스의 이름이다. 드라이버 벤더에 의존적이며 일반적으로 DataSourceClassName 값과 동일하다. (Deprecated)
<database-name>DB의 이름을 설정한다. Oracle의 경우 SID로 설정한다.
<service-name>Oracle inet 드라이버 사용하는 경우만 사용하며 Oracle의 SID 값을 설정한다. (Deprecated)
<description>데이터소스를 설명하는 내용의 텍스트를 설정한다.
<network-protocol>

DB에 연결할 때 사용되는 프로토콜을 설정한다. (Deprecated)

(예: Sybase의 "Tds")

<password>

사용자의 패스워드를 설정한다.

패스워드를 암호화해서 사용할 경우 {algorithm}ciphertext 형식으로 입력한다.

(예: {DES}FQrLbQ/D801DL28rw==)

허용되는 알고리즘 정보는 JEUS Security 안내서”의 “제2장 보안 시스템의 설정”에 설명되어 있다.

<user>사용자의 이름을 설정한다.
<port-number>DB Listener의 포트 번호를 설정한다.
<server-name>DB가 운용 중인 서버의 DNS 이름이나 IP주소를 설정한다.
<driver-type>

Oracle의 경우 드라이버의 타입을 설정한다. (Deprecated)

(예: thin, oci)

<property>JDBC 커스텀 프로퍼티를 지정한다. 자세한 내용은 다음에 나오는 “8.3.3. Custom DB 속성 추가”를 참고한다.
<connection-pool>Connection Pooling에 특화된 내용을 설정해준다. 자세한 내용은 “8.3.4. Connection Pool 설정”를 참고한다.
<auto-commit>

커넥션에 지정될 auto commit 값을 지정한다. (true/false)

로컬 XA 데이터소스나 XA 데이터소스의 경우에는 커넥션이 트랜잭션에 연동되어 있지 않을 경우에만 적용한다.

<stmt-query-timeout>

JDBC 커넥션에서 java.sql.Statement 객체를 생성할 때 일괄적으로 타임아웃을 적용한다. 만약 이 타임아웃을 적용하는 시점에 남아있는 트랜잭션 타임아웃이 더 짧은 것으로 판단이 되면 이 타임아웃이 아니라 남아있는 트랜잭션 타임아웃을 적용하도록 최적화한다.

JDK 1.4 이상에서 JDBC 3.0을 지원하는 드라이버에서 사용 가능하다. JEUS에서 제공하는 'Session Kill 기능(dba-timeout)' 대신 이 옵션을 사용하기를 권장한다. 이에 관한 자세한 사항은 "DB Session Kill 기능 : DBA 위임 데이터소스 (DBA Delegation Datasource)"를 참고한다.

<action-on-connection-leak>

컴포넌트(주로 Stateless 컴포넌트 - 서블릿/JSP, Stateless Session Bean, MDB)에서 사용한 JDBC 커넥션에 대한 Logging이나 반환 액션을 설정한다. 설정하지 않았을 경우 기본 동작은 엔진 컨테이너에 설정한 invocation-manager-action을 따른다.

invocation-manager-action에 대한 설명은 “1.4.4. Invocation Manager”를 참고한다.

<login-timeout>

DB와 connection을 맺을 때 login 과정에서의 timeout을 설정한다.

(기본값: 0, 단위: sec)


주의

<data-source-name>, <service-name>, <network-protocol>, <driver-type> 등 특정 JDBC 드라이버에 의존적인 프로퍼티들은 Custom 프로퍼티 설정으로 대체하길 권장한다.

데이터소스 사용자 이름 및 패스워드의 관리

JEUSMain.xml에 각 데이터소스 사용자 이름(<user>)과 패스워드(<password>)가 설정된다.

JEUS v6.0 Fix#8부터는 데이터소스 사용자 이름과 패스워드를 별도의 프로퍼티 파일에서 따로 관리하는 기능을 제공한다. 프로퍼티 파일의 설정이 JEUSMain.xml의 설정보다 우선하므로 프로퍼티 파일에 대하여 적절히 권한 설정을 하면 JEUS 관리자가 데이터소스 사용자 이름 및 패스워드를 변경하지 못하도록 제한할 수 있다.

다음의 옵션으로 프로퍼티 파일의 절대 경로를 설정하면 JEUS는 프로퍼티 파일에 정의된 데이터소스 사용자 이름과 패스워드를 가지고 데이터소스에 대한 Connection Pool을 구성한다.

-Djeus.jdbc.ds-accounts-properties-path

데이터소스의 구분은 JEUSMain.xml에서와 마찬가지로 데이터소스 ID(<data-source-id>)를 사용하여야 하므로 데이터소스 사용자 이름에 대한 프로퍼티 키는 data_source_id.user, 데이터소스 패스워드에 대한 프로퍼티 키는 data_source_id.password가 된다.

예제 [예 8.1]에 대하여 프로퍼티 파일은 다음과 같이 작성될 수 있으며 JEUS_HOME/config/ds-accounts.properties.template으로 프로퍼티 파일 템플릿을 제공하고 있으니 프로퍼티 파일을 작성할 때 참고한다.

[예 8.2] ds-accounts.properties

datasource1.user=foo
datasource1.password=bar


8.3.3. Custom DB 속성 추가

기본 설정으로 부족할 때 <database> 구성에 Custom 프로퍼티를 추가하여 해당 프로퍼티를 가진 DB 드라이버를 사용할 수 있다. 각각의 DB마다 요구하는 프로퍼티와 그 값이 다르므로 각 벤더의 JDBC 드라이버 매뉴얼에서 필요한 프로퍼티를 찾도록 한다.

다음 예제와 같이 <property> 태그에 다음과 같이 설정한다.

[예 8.3] Custom DB 속성 추가 : <<JEUSMain.xml>>

<jeus-system>
    . . .
    <resource>
        <data-source>
            <database>
                . . . <!-- See earlier sub-section -->
                <property>
                    <name>portNumber</name>
                    <type>java.lang.Integer</type>
                    <value>1099</value>
                </property>
                <property>
                    <name>driverType</name>
                    <type>java.lang.String</type>
                    <value>thin</value>
                </property>
            </database>
            . . .
        </data-source>
    </resource>
</jeus-system>


다음의 3가지 항목 설정으로 각각의 새로운 속성을 추가할 수 있다.

항목설명
<name>속성의 이름이다.
<type>java.lang.String, 원시 타입(Primitive type)의 래퍼 클래스(예: java.lang.Integer), java.util.Properties가 올 수 있다.
<value>속성에 문자열을 설정한다.

위의 예처럼 프로퍼티가 지정이 되면 JEUS는 벤더의 JDBC 드라이버에 setPortNumber라는 메소드를 호출하게 된다. 그러므로 사용자는 각 벤더의 매뉴얼 등을 참고하여 속성을 알맞게 설정하도록 한다.

참고

위에서 설명된 데이터소스를 적절히 구성해 주었다면, JEUS를 재시작해야 데이터소스를 애플리케이션에서 사용할 수 있다.

8.3.4. Connection Pool 설정

각 데이터소스는 향상된 성능을 위하여 Connection Pool 설정을 할 수 있다. 이는 JEUSMain.xml의 <database> 태그 아래 <connection-pool>을 사용하여 구성된다.

다음은 Connection Pool을 설정한 JEUSMain.xml 예제이다.

[예 8.4] Connection Pool 설정 : <<JEUSMain.xml>>

<jeus-system>
    . . .
    <resource>
        <data-source>
            <database>
                . . . <!-- See earlier sub-section -->
                <connection-pool>
                    <pooling>
                        <min>2</min>
                        <max>20</max>
                        <period>500000</period>
                    </pooling>
                    <wait-free-connection>
                        <enable-wait>true</enable-wait>
                        <wait-time>60000</wait-time>
                    </wait-free-connection>
                    <max-use-count>30</max-use-count>

                    <!-- check-query related configurations -->
                    <check-query>SELECT 1 FROM DUAL</check-query>
                    <non-validation-interval>5000</non-validation-interval>
                    <check-query-period>1000000</check-query-period>
                    <destroy-policy-on-check-query>
                       AllConnections
                    </destroy-policy-on-check-query>
                    <stmt-caching-size>10</stmt-caching-size>
                    <connection-trace>
                        <enabled>true</enabled>
                    </connection-trace>
                    <use-sql-trace>true</use-sql-trace>

                    <!-- When using DB2 XA data source -->
                  <keep-connection-handle-open>false</keep-connection-handle-open>
                </connection-pool>
            </database>
            . . .
        </data-source>
    </resource>
</jeus-system>


다음은 설정된 태그에 대한 설명이다.

  • <pooling>

    • Connection Pool 옵션들은 <connection-pool> 태그의 하위에서 <pooling> 태그로 묶인다.

    • 해당 옵션들은 다음과 같다.

      항목설명
      <min>Cache를 위한 DB 커넥션들의 최솟값(초기값)이다.
      <max>Cache를 위한 DB 커넥션들의 최댓값이다.
      <step>초기화된 후 커넥션 수를 늘릴 상황이 되면 커넥션이 이 값만큼 증가하게 된다.
      <period>

      여기 설정 된 period 값에 한 번씩 커넥션 수를 조정한다. 여기서 조정 대상이 되는 커넥션들은 idle 커넥션이 된다.

      만약 현재 커넥션이 앞서 지정된 <min> 값보다 작을 경우에는 Idle 커넥션 수를 <min> 값만큼 늘려주게 되며, 그 반대의 경우 Idle 커넥션 수를 줄여 <min> 값에 맞춰주게 된다. 만약 Idle 커넥션이 없을 경우에는 min 값만큼 줄이는 작업은 하지 않는다. 원활한 DB 작업을 위하여 이 값은 충분히 잡아주도록 한다.

      (기본값: 30분, 단위: ms)

  • <wait-free-connection>

    • Pool 안에 Idle 커넥션이 있지 않을 경우 사용된다. 이 태그 역시 <connection-pool>의 하위에서 설정된다.

      항목설명
      <enable-wait>

      이 태그는 Pool안에 이용 가능한 커넥션이 없고 현재 커넥션들이 이미 최댓값에 도달했을 때 DB 커넥션 요청을 처리하는 방법을 결정한다.

      • true : 시스템은 이용 가능한 커넥션을 얻기 위해 대기한다. 만약 대기 후에도 커넥션을 가져오지 못한다면 exception이 발생한다.

      • false : 커넥션을 새로 만들어서 애플리케이션에 제공하고 애플리케이션이 이를 반납하면 Pooling하지 않고 닫아서 버린다. 이렇게 한번 쓰고 버린다는 의미로 JEUS에서 'disposable 커넥션'이라고 한다.

      <wait-time>이 태그는 <enable-wait>가 true일 때만 유효하다. 이것은 사용자가 커넥션을 위해 대기하는 시간을 나타낸다. 만약 어떠한 커넥션도 사용자가 이 시간 동안 대기해서 이용할 수 없을 때는 시스템은 사용자에게 Exception을 출력한다. (단위: ms)
  • <delegation-datasource>

    • 현재의 XA-Datasource의 전역/지역 트랜잭션을 NonXA-Datasource로 전송할 때 사용한다.

    • 데이터소스가 XA 또는 로컬 XA일 때 트랜잭션으로 묶이지 않는 상황에서 커넥션을 얻어올 경우 여기에 설정 된 데이터소스에서 커넥션을 가져온다. 그러므로 이 태그에 해당하는 값은 ConnectionPoolDataSource 타입으로 설정 된 데이터소스의 export name이어야 한다. 자세한 내용은 "DB Session Kill 기능 : DBA 위임 데이터소스 (DBA Delegation Datasource)"부분을 참고한다.

  • <max-use-count>

    • 한 개의 커넥션에 대해서 설정된 숫자만큼 사용하게 되면 해당 커넥션을 버리고 새로운 커넥션을 맺게 된다. (기본값: '0', 커넥션들을 교체하지 않겠다는 의미이다)

  • <delegation-dba>

    • DBA 권한을 갖는 특별한 데이터소스를 지정한다. 설정값은 지정 데이터소스의 export name을 사용한다. (예: Tibero,Oracle, Sybase)

  • <dba-timeout>(deprecate)

    • Connection Pool에서 얻은 커넥션을 여기에서 지정한 시간이 지난 후에도 반납하지 않으면 <delegation-dba>에서 설정된 데이터소스를 통해 session kill 명령을 보내서 해당 커넥션과 DB 세션을 강제로 닫아준다. 자세한 내용은 "DB Session Kill 기능 : DBA 위임 데이터소스 (DBA Delegation Datasource)"부분을 참고한다. (기본값: 무제한(-1), 단위: ms)

    • 이 기능은 꽤 위험하고 대체 기능이 있기 때문에 사용하기를 권장하지 않는다.

  • <check-query>

    • 애플리케이션이 getConnection할 때 JDBC 커넥션에 문제가 있는지 체크할 때 사용된다.

    • JDBC 커넥션의 내부적인 에러로 인한 끊김, 방화벽에 의한 소켓 끊김 현상 등을 체크할 때 유용하다. 체크가 실패하면 물리적 커넥션을 새로 만들어서 그에 대한 핸들을 애플리케이션으로 리턴한다. 클러스터 데이터소스를 사용하는 경우, 데이터소스의 상태 체크에 이용하므로 반드시 설정해야 한다.

      (예: SELECT 1 FROM DUAL)

  • <check-query-timeout>

    • <check-query>를 했을 때 DB 상황에 따라서 JDBC 드라이버가 응답을 못 받고 계속 기다릴 수 있다. 이런 문제를 방지하기 위하여 JDBC 스펙에 정의된 query timeout 설정을 사용하여 타임아웃을 줄 수 있다.

    • 타임아웃이 되면 커넥션이 무효한 것으로 판단한다. 참고로 이를 제대로 지원하지 않는 드라이버가 있을 수 있다.

  • <non-validation-interval>

    • <check-query>를 수행할 때의 시각과 맨 마지막에 점검한 시각과의 차이가 이 Interval 내에 있다면 <check-query>를 수행하지 않는다.

    • 커넥션 요청이 빈번할 경우 <check-query>가 너무 잦아져서 오버헤드를 줄 수 있는데 이 값을 적절하게 설정하면 체크 횟수를 줄일 수 있다.

  • <check-query-period>

    • JDBC 커넥션을 일정 시간마다 체크하여 문제가 있는 커넥션을 닫아준다. (단위: ms)

    • 이 기능을 사용하려면 <check-query>가 반드시 설정되어야 한다. 클러스터 데이터소스를 사용하는 경우 데이터소스의 상태 체크에 이용하므로 반드시 설정해야 한다. 이 값은 충분히 크게 지정한다.

  • <check-query-retrial-count>

    • <check-query>는 기본적으로 한 번만 수행하지만 이것이 부족하다고 판단될 경우에는 이 설정을 통해서 체크 횟수를 늘릴 수가 있다.

  • <check-query-class>

    • <check-query> 기능의 확장을 위해 존재한다.

    • 이 태그에는 jeus.jdbc.connectionpool.JEUSConnectionChecker를 구현한 클래스의 full name을 입력한다. 그렇게 할 경우 JEUS는 사용자가 지정한 클래스를 이용하여 <check-query>를 실행한다. 자세한 내용은 "커넥션 점검(Connection Validation or Check Query)"을 참고한다.

  • <destroy-policy-on-check-query>

    • JDBC 커넥션 유효성 체크가 실패했을 경우 해당 Connection Pool의 커넥션들을 어떻게 할지 정책을 결정하는 옵션이다.

  • <stmt-caching-size>

    • PreparedStatement 객체를 얼마나 Cache할 지 지정한다. 기본값으로는 이 기능을 사용하지 않는다.

    • Oracle 9i 이후 버전에서는 사용하지 않도록 한다. 다른 DBMS에서는 시스템 리소스의 낭비를 줄임으로써 성능 향상을 가져 올 수 있다.

  • <stmt-fetch-size>

    • 여기에 지정된 값은 직접 하부 DB의 커넥션 객체에 저장 되는 값으로서 커넥션의 row fetch 값을 지정한다.

    • 현재 JEUS에서는 Oracle과 Sybase에서 이 기능을 제공한다.

  • <connection-trace>

    • 커넥션이 보여줄 수 있는 정보들을 생성해 둘 것인지 설정하는 옵션이다.

    • 현재는 invocation manager를 설정했을 때 또는 jeusadmin의 dsconinfo 명령을 통해서 현재 커넥션을 사용하고 있는 애플리케이션을 Thread Stack으로 확인할 수 있는 기능을 제공한다.

      하위 태므설명
      <enabled>이 태그는 connection trace 기능을 on/off 하는 옵션을 나타낸다.
  • <keep-connection-handle-open>

    • 물리적 커넥션을 Pooling하는 동안 커넥션 핸들(또는 논리적 커넥션)을 항상 열어두고 사용하고자 할 때 필요한 옵션이다.

    • DB2 Universal Driver에서 XA 데이터소스를 사용할 경우에 이 옵션을 true로 설정하길 권장한다.

      IBM DB2에서 제공하는 Universal Driver(JCC) type 4는 2007년 11월에 XA 데이터소스에 문제가 있음을 확인하였다. 여러 Thread에서 서로 다른 물리적 커넥션을 가져가서 핸들(또는 논리적 커넥션)을 'get(open) & close'하면서 사용하다 보면 드라이버 내부적으로 무한루프에 빠진다. 이때 Thread Stack Dump를 확인하면 내부적으로 같은 Vector(java.util.Vector)를 공유해서 사용하고 있는데 한 스레드는 Vector에 대한 Lock을 설정하고 'Runnable'인 상태이고 다른 Thread들은 그 Lock을 기다리게 된다.

      DB2 내부 로직을 알 수 없기에 정확한 원인은 알 수 없지만 테스트 결과 커넥션 핸들을 항상 열어두면 문제가 발생하지 않는 것을 확인하였다. 커넥션 핸들을 항상 열어두면 내부 공유 Vector에 접근하는 일이 맨 처음 생성할 때와 물리적 커넥션을 닫을 때 외에는 없기 때문이다. 이를 설정하는 방법은 JEUS v6.0 Fix#2부터 추가되는 옵션인 <keep-connection- handle-open>을 true로 설정한다.

      DB2 드라이버 3.53.95부터 버그가 수정되어서 <keep-connection- handle-open>을 사용하지 않아도 제대로 동작한다. 이에 대한 IBM 공식 버그 리포트 넘버는 APAR IZ41181이며, DB2 9.5 Fixpak 4 문서에서 확인할 수 있다. 실제로는 JEUS와 LIG 손해보험을 통해서 문제가 제기되었지만 WebLogic 9.2에서 문제가 발생한 것처럼 되어 있다.

  • <init-sql>

    • 커넥션이 생성된 후 가장 처음으로 수행해야 하는 SQL이 있을 때 설정해서 사용한다.

    • <keep-connection-handle-open> 기능을 사용하면 <init-sql>을 수행할 때 사용했던 <connection-handle>을 계속 열어두고 사용한다.

  • <use-sql-trace>

    • 애플리케이션이 사용한 SQL문을 로그에 남기거나 현재 커넥션에서 수행되고 있는 SQL을 확인할 수 있는 기능이다.

    주의

    이 기능을 사용하게 되면 항상 JDBC Statement 객체에 대한 래퍼를 사용하게 되므로 JDBC 드라이버의 Statement 객체를 캐스팅해서 사용할 경우에는 이 기능을 사용할 수 없다.

커넥션 점검(Connection Validation or Check Query)

애플리케이션이 JDBC 커넥션 요청을 했을 때(getConnection method) 특정 select 쿼리를 보내서 커넥션의 상태를 점검(validation)하는 기능이다. JDBC 커넥션의 내부적인 에러로 인한 끊김, 방화벽에 의한 소켓 끊김 현상 등을 체크할 때 유용하다. 점검이 실패하면 물리적 커넥션을 새로 생성하여 그에 대한 핸들을 애플리케이션으로 리턴한다. 만약 RAC를 위한 데이터소스 클러스터링을 사용하는 경우 이것을 반드시 설정해야 한다.

  • <check-query>

    • Check Query 기능은 크게 2가지로 설정할 수 있다.

      • 단순히 설정상의 <check-query> 태그에 Query문을 넣는 방법

      • <check-query-class> 태그를 이용하여 기능을 확장

    • <check-query>를 위한 Query문은 DB에 업데이트를 하는 명령이 아닌 단순히 쿼리만을 위한 명령어를 넣어야 한다. 그렇지 않을 경우<check-query>를 위해 DB Lock을 설정하기 때문에 이후 업데이트를 위한 작업들이 원활히 수행되지 않을 수도 있다.

      <check-query>SELECT 1 FROM dual</check-query>
  • <check-query-timeout>

    • Check Query를 수행했을 때 DB 서버가 응답이 없어 드라이버가 계속 기다리는 상황이 발생할 수 있다. 이런 경우를 피하기 위해 <check-query>에 대해서 타임아웃을 설정할 수 있다. 이것은 JDBC에서 제공하는 'statement query timeout' 기능을 이용하는 것이다.

      <check-query-timeout>15000</check-query-timeout>

      설정단위는 ms이며, 1000ms보다 적을 경우 결국 0이 설정되므로 주의한다.

  • <non-validation-interval>

    • Check Query가 너무 잦아져서 오버헤드가 발생하는 경우 <non-validation-interval> 설정한다. 이는 Check Query를 수행할 때의 시각과 맨 마지막에 커넥션을 사용한 시각과의 차이가 설정한 Interval 내라면 Check Query를 하지 않도록 하는 설정이다.

    • 예를 들어 <non-validation-interval>을 5초(5000 ms)라고 설정했을 때 어떤 커넥션을 Pool에서 꺼내서 마지막에 사용했던 시간이 아직 5초가 지나지 않았다면 그 커넥션이 유효하다고 가정하고 Check Query를 수행하지 않는 것이다.

      <non-validation-interval>5000</non-validation-interval>
  • <destroy-policy-on-check-query>

    • 사용자는Check Query가 실패했을 때 해당 Connection Pool에 있는 나머지 커넥션들에 대한 destroy 정책을 결정할 수 있다.

      정책은 다음과 같다.

      구분설명
      FailedConnectionOnlyCheck Query가 실패한 커넥션만 버린다.(기본값)
      AllConnections나머지 커넥션들도 모두 버린다. 만약 정책을 'AllConnections'로 했을 경우에는 Check Query가 실패하자마자 바로 커넥션들을 버리는 것이 아니라 한 번 더 커넥션을 Pool에서 꺼내서 Check Query를 시도한다. 그마저도 실패하면 비로소 Pool에 있는 모든 커넥션들을 버린다.
    • 다음은 Check Query가 실패하는 경우 Pool에 있는 커넥션에 대한 destroy 정책을 설정한 예이다.

      <destroy-policy-on-check-query>AllConnections</destroy-policy-on-check-query>
  • <check-query-retrial-count>

    • Check Query를 추가적으로 더 하도록 설정하고 싶을 경우에는 <check-query-retrial-count>를 이용해서 설정할 수 있다.

Check Query 클래스는 응용 프로그램 개발자나 사용자는 위에서 제시한 jeus.jdbc.connectionpool.JEUSConnectionChecker interface를 구현함으로써 기능을 사용자가 확장할 수 있도록 한다.

[예 8.5] jeus.jdbc.connectionpool.JEUSConnectionChecker

public interface JEUSConnectionChecker {
   void checkConnection(Connection vcon) throws SQLException;
   void setQueryString(String query);
   void setQueryTimeout(int timeout);
}


메소드설명
checkConnection()이 메소드는 실제 Check Query 작업을 수행할 때 불려진다. 그러므로 개발자는 이 메소드에 자신이 Check Query할 때 실행하고 싶은 내용들을 구현하여 입력한다.
setQueryString()<check-query> 태그에 Query 문이 지정되어 있는 상태라면, JEUS는 이 메소드를 호출한다. 물론 인자로는 <check-query> 태그에 지정된 Query 문이 들어가게 된다.
setQueryTimeout()<check-query-timeout>이 지정되어 있는 상태라면 JEUS는 이 메소드를 호출한다.

Check Query 클래스가 지정이 되지 않은 상태라면, JEUS는 지정된 Query 문만을 이용하여 Check Query를 수행한다. 즉, Check Query 클래스의 사용은 선택을 할 수 있으며, 단순히 Query 문을 이용하는 것이 아닌 특별한 작업이 필요한 경우에만 사용하도록 한다.

SQL Trace

커넥션별로 사용하고 있는 SQL 문을 조회하는 기능이다. 시스템 로그를 통해서 SQL history를 조회할 수 있고, jeusadmin을 통해서 커넥션별로 현재 수행되고 있는 SQL을 확인할 수 있다. 서버 로그 상에는 jeus.jdbc.sql 로거의 레벨을 FINE으로 설정할 경우 보이게 된다. 그리고 이 기능을 사용할 경우 JDBC 드라이버의 Statement 객체를 항상 wrapping하게 되므로 JDBC 드라이버의 Statement 객체를 캐스팅해서 사용하는 애플리케이션은 이 기능을 사용할 수 없다.

Statement Caching

PreparedStatement는 미리 SQL을 Parsing해 놓은 인스턴스를 애플리케이션 입장에서 계속 재활용해서 사용할 수 있으므로 매번 드라이버가 SQL을 Parsing하기 위해 들이는 오버헤드를 줄일 수 있다. 그런데 이러한 PreparedStatement 역시 커넥션을 새로 얻을 때마다 SQL Parsing 작업을 해야 하므로 커넥션 요청(getConnection & close)이 빈번하게 이뤄지는 경우에는 PreparedStatement 인스턴스를 생성하는 것 역시 큰 오버헤드가 될 수 있다. 따라서 JEUS에서는 이런 오버헤드를 줄이기 위해 Statement Caching 기능을 제공한다.

이것을 사용하면 물리적 커넥션별로 SQL 문장을 키로 하여 PreparedStatement 객체를 저장해놓고 애플리케이션이 PreparedStatement를 요청할 경우에 파라미터로 넘어온 SQL을 보고 미리 생성된 것이 있으면 그것을 리턴한다.

그런데 커넥션 인스턴스에서 PreparedStatement를 생성하기 때문에 그 커넥션 인스턴스를 닫아버리면(close) 해당 PreparedStatement 인스턴스 역시 무효(invalid)로 된다. 따라서 애플리케이션이 커넥션을 닫았다고 하더라도 실제로 WAS에서는 이 커넥션을 계속 열어둔 채로 재활용하도록 되어 있다. 그래야만 PreparedStatement 인스턴스를 계속 재활용할 수 있기 때문이다.

...
   Connection conn = dataSource.getConnection();
   PreparedStatement stmt = conn.prepareStatement(sql);
   ....
   stmt.close();
   conn.close();
....

주의

애플리케이션은 Statement Caching의 제약사항을 반드시 숙지해야 한다. 즉, 커넥션을 항상 열어둔 채로 사용하기 때문에 커넥션을 닫았을 때 드라이버가 해주는 클리어 작업이 이뤄지지 않는다. 예를 들어 Oracle JDBC 드라이버의 경우 <auto-commit>을 false로 해놓고 사용하다가 Commit이나 Rollback을 하지 않고 커넥션을 닫으면 무조건 Commit을 하도록 되어 있는데 이런 처리가 되지 않는다는 것이다. <keep-connection-handle-open> 옵션 또한 같은 제약사항이 있으므로 주의한다.

위임 데이터소스

일반적으로 XA 데이터소스가 사용되면 대부분 트랜잭션으로 묶여 작업이 이루어진다. 하지만 일부의 경우에는 트랜잭션이 시작되기 전이나 끝난 후에 커넥션을 가져다 사용하는 경우가 있을 수 있다. 이런 상황에서는 트랜잭션과 무관하게 커넥션이 사용되므로 Connection Pool 형식의 데이터소스에서 가져온 커넥션과 동작에 있어 아무런 차이가 없게 된다.

그런데 Oracle, DB2 등의 JDBC 드라이버에서는 XA 커넥션을 트랜잭션 없이 사용도 하고 트랜잭션에 연동도 하면서 사용하다 보면 XA를 시작할 수 없는 예외가 발생한다. 정확한 원인은 알 수 없기 때문에 이를 피해 가기 위해서 위임 데이터소스를 사용할 수 있다.

위임 데이터소스(Delegation Datasource)는 XA 형식으로 지정된 데이터소스가 트랜잭션과 무관한 곳에서 커넥션 요청을 받을 경우 이용하게 되는 데이터소스이다. 이 상황에서 사용자가 커넥션 요청을 할 경우 XA 데이터소스는 위임 데이터소스로 지정된 Connection Pool 데이터소스에서 커넥션을 가져와 사용자에게 넘겨주게 된다.

이 기능을 사용하기 위해서는 우선 XA 데이터소스와 같은 DB에 대한 Connection Pool 데이터소스를 지정한다. 그리고 <delegation-datasource> 태그 내부에 설정한 Connection Pool 데이터소스의 JNDI 이름을 입력한다.

DB Session Kill 기능 : DBA 위임 데이터소스 (DBA Delegation Datasource)

<delegation-datasource>, <dba-timeout>에서 설명한 것처럼 DBA 위임 데이터소스는 커넥션에 대응하는 DB 세션을 강제로 정리해야 할 필요가 있을 때 사용한다. ConnectionPool에서 커넥션을 얻어간 뒤 오랜 시간동안 Pool로 반납되지 않으면 해당 커넥션이 DB에 부하를 주거나 장애를 일으킬 것으로 판단하고 강제로 DB를 정리를 하고자 사용하던 기능이다.

JEUS v6.0 Fix#6에서는 WebAdmin을 통해서 DB 커넥션 강제 정리 명령을 내릴 때도 DBA 위임 데이터소스가 설정되어 있다면 이를 이용해서 DB에 Session Kill 명령을 내리도록 하였다. 기본적으로 이 기능은 DB에 직접 operation을 하기 때문에 사용할 때 주의가 필요하지만 다음의 예제에서 <dba-timeout>을 설정하면 어떻게 작동하는지 간략하게 설명한다.

우선 사용하는 데이터소스 외에 Connection Pool 형식의 새로운 데이터소스를 설정한다. 그 데이터소스 이름이 DBKiller라고 한다면 사용하고 있는 데이터소스에 다음과 같이 설정한다.

[예 8.6] <dba-timeout> 설정

.....
<connection-pool>
    <delegation-dba>DBKiller</delegation-dba>
    <dba-timeout>60000</dba-timeout>
</connection-pool>
.....

만약 현재 사용하는 데이터소스에서 얻어진 커넥션이 <dba-timeout> 태그에 지정 된 60초(단위: ms) 내에 반납이 되지 않으면, JEUS JDBC는 DBKiller 데이터소스에 반납되지 않은 커넥션의 세션 ID를 죽이라는 kill 명령을 내린다. 그렇게 되면 DBKiller 데이터소스는 해당 커넥션을 강제로 닫아주게 되게 된다. 하지만 응용 프로그램이 명시적으로 close 명령을 내리지 않으면 해당 커넥션은 active인 상태로 남아있게 된다. close를 호출하면 커넥션을 버리고 새로 커넥션을 맺어서 Pool에 넣어둔다.

이 기능을 사용하기 위해서는 DB가 이 기능을 지원해야 한다. JEUS에서는 단순히 해당 DB에 세션 제거를 요청하는 Query를 요청하고, DB가 해당 Query를 받아 동작을 수행한다. 자세한 내용은 DB 벤더에서 제공된 매뉴얼을 참고하여 기능의 지원 여부를 따져보도록 한다. 또한 DBA 위임 데이터소스는 실제 DBA 대상이 되는 데이터소스와 같은 DB에 설정이 되어있어야 한다.

주의

1. 이 기능은 JDBC 2.0 이하 드라이버에서 select query 등이 너무 오래 걸릴 때 그것을 끊어줄 방법이 없어서 사용한 방법이다. 그러나 JDBC 3.0 또는 그 이상의 버전을 구현한 드라이버는 statement query timeout 옵션을 제공하므로 더이상 Session Kill 기능을 사용할 필요가 없다. 대신 <database><stmt-query-timeout> 설정의 사용을 권장한다.

2. XA 데이터소스에 설정하면 안 된다. 왜냐하면 XA가 정상적으로 진행하는 도중에 Session Kill이 일어날 수 있으며, 커넥션 공유 상황에서는 Session Kill이 발생한 이후에 제대로 Rollback이 되지 않을 수 있기 때문이다. 따라서 XA 데이터소스에는 statement query timeout을 사용해야 하며, 추가적으로 컨테이너 설정에 트랜잭션 타임아웃을 적절하게 설정하여 사용하길 권장한다.

8.3.5. 클러스터 데이터소스 설정

JEUS 차원에서 RAC 인스턴스 간의 장애 복구(failover and failback)를 제공하기 위해서 클러스터 데이터소스를 설정할 수 있다. 이 설정은 <resource> <data-source> <cluster-ds> 태그를 사용해서 설정한다.

다음은 클러스터 데이터소스 설정에 대한 예제이다.

[예 8.7] 클러스터 데이터소스 설정 : <<JEUSMain.xml>>

<jeus-system>
    . . .
    <resource>
        <data-source>
            <database>
                <export-name>RAC1</export-name>
                . . .
            </database>
            <database>
                <export-name>RAC2</export-name>
                . . .
            </database>
            . . .
            <cluster-ds>
                <export-name>RAC</export-name>
                <data-source>RAC1</data-source>
                <data-source>RAC2</data-source>
            </cluster-ds>
        </data-source>
    </resource>
</jeus-system>

다음은 설정 태그에 대한 설명이다.

태그설명
<data-source-id>클러스터 데이터소스의 ID를 설정한다.
<export-name>클러스터 데이터소스의 JNDI 이름을 설정한다.
<data-source-target>

클러스터 데이터소스를 사용할 엔진 컨테이너 이름을 설정한다.

설정하지 않으면 노드에 속한 모든 엔진 컨테이너에서 사용할 수 있다.

<data-source-selector>

클러스터 데이터소스로부터 커넥션을 얻을 때, 클러스터 데이터소스에 참여하고 있는 특정 데이터소스 선택에 대한 정책을 정의하는 클래스의 이름(Fully Qualified Class Name)을 설정한다.

자세한 사용법은 "데이터소스 셀렉터 사용하기"에서 설명한다.

<load-balance>

getConnection을 요청하는 경우 Round-Robin 방식으로 데이터소스를 선택하고 선택한 데이터소스에서 커넥션을 얻어 리턴한다.

JEUS v6.0 Fix#7에 추가된 기능이다.

<use-failback>Failback 기능을 사용할 것인지 선택하는 옵션이다. 이전에는 Failover만을 지원했기 때문에 호환성을 위해서 만든 옵션이다. (기본값: true)
<is-pre-conn>

  • true: 클러스터 데이터소스에 참여하는 모든 데이터소스의 커넥션을 미리 연결해 놓는다. 이렇게 될 경우 ㄹFailover 성능은 향상될 수 있으나 시스템 리소스가 많이 이용되는 단점이 있다.

  • false: 클러스터 데이터소스에 참여하는 데이터소스의 커넥션을 필요시 lazy하게 생성한다. 특히 Failover-Failback 방식에서는 Failback 이후 idle 상태가 된 backup 데이터소스의 Connection Pool을 destroy하여 시스템 리소스를 절약할 수 있지만 필요시 매번 다시 생성해야 하는 부담을 지게 된다. (기본값)

<data-source>

클러스터 데이터소스에 포함되는 데이터소스의 <export-name>을 입력한다.

리스트의 첫 번째가 주 RAC 인스턴스가 되고, 이것이 Down되면 다음 인스턴스가 선택된다. 다음의 설정 예제의 경우 처음에는 RAC1 데이터소스에서 커넥션을 가져오고 커넥션을 가져오는 도중 Exception이 발생할 경우(Wait timeout인 경우는 제외) RAC2 데이터소스를 사용한다.

클러스터 데이터소스에 참여하는 데이터소스의 설정

JEUS v6.0 Fix#3부터는 자동으로 Failback하는 기능이 포함된다. 클러스터 데이터소스에서 Failback을 하기 위해서는 참여하는 모든 데이터소스에 반드시 <check-query><check-query-period>를 설정해야 한다.

예제에서 주 데이터소스는 RAC1이고 백업 데이터소스가 RAC2이다. 만약 RAC1이 죽었거나 문제가 생겼을 경우에는 애플리케이션이 커넥션을 가져갈 때(getConnection) <check-query>에 의해 이를 감지하게 되며 Failover를 통해서 RAC2 데이터소스를 사용하게 된다. 그리고 RAC1 데이터소스가 살아났는지 <check-query-period>에 설정한 주기마다 체크한다. RAC1 데이터소스가 살아난 후부터는 모든 커넥션 요청이 자동으로 RAC1 데이터소스로 가게 된다.

만약 예전처럼(JEUS v6.0 Fix#2 이하) Failover만을 사용해야 할 경우에는 <cluster-ds><use-failback>을 false로 할 수 있다.

참고

수동으로 Failback 명령을 내릴 수 있는데 이에 대해서는 "JEUS Reference Book"에서 controlcds 명령을 참고한다.

Check Query가 실패하는 경우 Pool에 있는 커넥션에 대한 destroy 정책을 AllConnections로 설정한다. 그렇지 않으면 만약 RAC1이 죽고 RAC2로 Failover 되었을 경우 RAC1에는 이미 끊어진 커넥션들이 Pool에 계속 남아있게 된다. 물론 주기적인 <check-query>에 의해서 정리가 되지만 Failover가 이뤄지는 시점에 끊어진 커넥션들이 정리가 되는 것이 좀 더 바람직하기 때문이다.

[예 8.8] 자동으로 Failback 설정 : <<JEUSMain.xml>>

<jeus-system>
    . . .
    <resource>
        <data-source>
            <database>
                <export-name>RAC1</export-name>
                . . .
                <check-query>select 1 from dual</check-query>
                <check-query-period>300000</check-query-period>
                <destroy-policy-on-check-query>
                AllConnections
                </destroy-policy-on-check-query>
            </database>
            <database>
                <export-name>RAC2</export-name>
                . . .
                <check-query>select 1 from dual</check-query>
                <check-query-period>300000</check-query-period>
                <destroy-policy-on-check-query>
                AllConnections
                </destroy-policy-on-check-query>
            </database>
            . . .
            <cluster-ds>
                <export-name>RAC</export-name>
                <data-source>RAC1</data-source>
                <data-source>RAC2</data-source>
            </cluster-ds>
        </data-source>
    </resource>
</jeus-system>


데이터소스 셀렉터 사용하기

JEUS v6.0 Fix#9부터는 클러스터 데이터소스로부터 커넥션을 얻을 때, 클러스터 데이터소스에 참여하고 있는 특정 데이터소스 선택에 대한 정책을 사용자나 개발자가 직접 정의할 수 있다. jeus.jdbc.helper.DataSourceSelector 인터페이스를 구현함으로써 정책을 정의하고 <data-source-selector> 설정에 구현 클래스의 이름(Fully Qualified Class Name)을 설정한다.

jeus.jdbc.helper.DataSourceSelector API는 다음과 같이 정의되어 있다.

[예 8.9] jeus.jdbc.helper.DataSourceSelector

public interface DataSourceSelector {
   public void setComponentDataSourceList(List<String> componentDataSourceList);
   public String selectDataSource();
}


  • public void setComponentDataSourceList(List<String> componentDataSourceList)

    • 클러스터 데이터소스에 속한 컴포넌트 데이터소스들의 ID 리스트를 설정한다

  • public String selectDataSource()

    • 클러스터 데이터소스에 속한 컴포넌트 데이터소스 선택에 대한 정책 정의를 위해 구현해야 하는 메소드이다. 리턴값은 정의된 정책을 통하여 선택된 데이터소스의 ID가 되어야 한다. 대부분의 환경에서 클러스터 데이터소스로부터 커넥션을 얻을 때는 다수의 요청 Thread들이 동시에 접근할 것이므로 정책을 정의할 때는 대체로 동기화에 대한 고려가 필요하며 그것은 구현자의 몫이다.

위의 API를 바탕으로 다음과 같은 customized DataSourceSelector 구현이 가능하다. 참고로 이 DataSourceSelector 구현체는 클러스터 데이터소스에 참여하는 데이터소스들에 대하여 2대 1의 선택 비율을 정의하고 있으며 동기화를 고려하여 selectDataSource 메소드를 synchronized로 구현하였다.

[예 8.10] foo.bar.SampleDataSourceSelector

package foo.bar;

import jeus.jdbc.helper.DataSourceSelector

public class SampleDataSourceSelector implements DataSourceSelector {
   List<String> componentDataSourceList = new ArrayList<String>();

   // 동기화 보장
   AtomicInteger dataSourceIndex = new AtomicInteger(0);

   public void setComponentDataSourceList(List<String> componentDataSourceList) {
      this.componentDataSourceList.addAll(componentDataSourceList);
   }

   public String selectDataSource() {
      int reminder = (dataSourceIndex.getAndIncrement() & 0x7fffffff) % 3;
      if(reminder < 2) {
         return componentDataSourceList.get(0);
      }
      else {
         return componentDataSourceList.get(1);
      }
   }
}


클러스터 데이터소스 설정은 다음과 같이 할 수 있다.

[예 8.11] <<JEUSMain.xml>>

<jeus-system>
    . . .
    <resource>
        <data-source>
            . . .
            <cluster-ds>
                <export-name>RAC</export-name>
                <data-source-selector>foo.bar.SampleDataSourceSelector</data-source-selector>
                <data-source>RAC1</data-source>
                <data-source>RAC2</data-source>
            </cluster-ds>
        </data-source>
    </resource>
</jeus-system>


DataSourceSelector를 사용할 경우 <load-balance> 설정은 아무런 기능을 하지 않으며 Failover와 Failback을 무조건 시도한다. Failover는 구현한 데이터소스 셀렉터를 통하여 최초 선택된 데이터소스 다음 인덱스의 데이터소스를 시작으로 한 번만 rounding하는 방식으로 이루어진다.

Failback은 <use-failback> 설정할 때와 유사한 방식으로 이루어지므로 클러스터 데이터소스에 참여하는 데이터소스들에 대하여 <check-query>와 <check-qeury-period> 설정을 반드시 해야 한다.

8.4. DB Connection Pool의 제어 및 모니터링

사용자는 DB Connection Pool을 실시간으로 제어하기 위해서 콘솔 툴(jeusadmin)이나 WebAdmin을 사용할 수 있다. WebAdmin에 대한 설명은 "JEUS WebAdmin 안내서"를 참조한다.

JEUS v6.0 Fix#7 이후로는 각 명령에서 사용하는 데이터소스 지칭 인자는 데이터소스의 JNDI Export 이름이 아니라 데이터소스의 ID임을 유념해야 한다. jeusadmin의 help -g jdbc 명령어를 사용하여 JDBC 관련 명령어들의 정보를 확인할 수 있다.

8.4.1. DB Connection Pool의 제어

JEUS의 주 콘솔 툴인 jeusadmin을 이용하여 DB 관련 설정 및 동작을 모니터링하거나 제어할 수 있다.

[blah@johan:/opt/jeus/bin] ./jeusadmin johan [options]

위의 명령을 사용하여 JEUS 노드에 접속을 한다. 그 후 몇 가지 Connection Pool 제어 명령을 수행할 수 있다.

  • Connection Pool 제어 명령 수행 후의 결과를 확인

    Connection Pool 제어 명령 수행 후의 결과를 확인하려면 “8.4.2. DB Connection Pool의 모니터링”을 참고하여 Connection Pool을 모니터링한다.

  • Connection Pool을 비활성화하기

    다음 명령어는 johan_container1에 설정 되어 있는 'datasource1'이라 명명된 Connection Pool을 불능 상태로 만들 때 사용된다.

    johan>disableds -con johan_container1 datasource1
  • Connection Pool을 활성화하기

    Pool을 활성화하려면 다음의 명령어를 적용한다.

    johan>enableds -con johan_container1 datasource1
  • Connection Pool 생성

    jeusadmin을 통해서 Connection Pool을 처음 생성하거나 Pool에 있는 커넥션을 모두 비우고 새로운 커넥션으로 채워넣을 수도 있다.

    처음 생성할 경우에는 다음 명령을 실행한다.

    johan>createds -con johan_container1 datasource2

    [-con] 옵션을 주지 않으면 모든 엔진 컨테이너의 커넥션을 Pool을 생성한다. 만약 container.name 프로퍼티가 설정되어 있다면 그 설정으로 지정된 엔진 컨테이너(들)의 Connection Pool(들)만 생성한다.

  • Connection Pool 새로 채우기

    현재 Pool에 있는 커넥션을 모두 버리고 새로운 커넥션으로 채워넣고 싶다면(DB를 재기동했거나 비정상적 종료를 해서 Pool에 있는 커넥션들이 모두 의미없는 상태가 된 경우 등) 다음의 명령을 실행한다.

    johan>refreshds -con johan_container1 datasource3

    엔진 컨테이너에 해당하는 옵션은 createds 명령과 같다.

  • DB Connection Pool 설정 테스트

    jeusadmin을 통해서 설정 테스트를 할 수도 있다.

    johan>testdsconfig datasource1

8.4.2. DB Connection Pool의 모니터링

본 절에서는 콘솔 툴(jeusadmin)으로 Connection Pool을 모니터링하는 방법을 간략하게 설명한다.

참고

WebAdmin에 대한 설명은 "JEUS WebAdmin 안내서"를 참고한다.

JEUS v6.0 Fix#7 이후로는 각 명령에서 사용하는 데이터소스 지칭 인자는 데이터소스의 JNDI Export 이름이 아니라 데이터소스의 ID임을 유념해야 한다.

  • 하나의 엔진 컨테이너에 구성된 JDBC Connection Pool을 모니터링

    jeusadmin에서 하나의 엔진 컨테이너에 구성된 JDBC Connection Pool을 모니터링하기 위해서 dsinfo 명령을 사용한다.

    만약 생성된 Connection Pool만 보고 싶다면 다음의 옵션을 설정한다. -k 옵션은 -i 옵션이 반드시 필요하며 -i 옵션만 지정했을 경우에는 모니터링 정보 출력을 무한히 반복하게 되는데 <Enter> 키를 두 번 눌러서 끝낼 수 있다.

    옵션설명
    -active생성된 Connection Pool만을 출력한다. 인자는 없다.
    -k

    몇 번 정보를 출력할지 결정한다.

    (예: dsinfo -k 20)

    -i

    몇 초에 한 번씩 정보를 출력할지 결정한다. 인자값의 단위는 초(sec)이다.

    (예:dsinfo -i 1)

    johan>dsinfo -con johan_container1
    =========================================================================================
    Connection pool information for engine container 'johan_container1'
    
    -----------------------------------------------------------------------------------------
    |          id |        jndi | min | max | act | idle | disp | tot | wait |  work |
    -----------------------------------------------------------------------------------------
    | datasource1 | datasource1 | 10  | 20  | 0   | 10   | 0    | 10  | true | false |
    | datasource2 | datasource2 | 10  | 30  | 0   | 0    | 0    | 0   | true | false |
    | datasource3 | datasource3 | 10  | 40  | 0   | 10   | 0    | 10  | true | true  |
    -----------------------------------------------------------------------------------------
    
    disp : disposable connection, tot(total) = act(active) + idle + disp
    =========================================================================================

    다음은 명령어 실행 결과의 출력되는 항목에 대한 설명이다.

    항목설명
    idConnection Pool에 대응되는 데이터소스의 ID로 별도로 설정하지 않았으면 jndi 필드의 값과 같다.
    jndiConnection Pool에 대응되는 데이터소스의 JNDI Export name으로 별도로 설정하지 않았으면 ID 필드의 값과 같다.
    minPool 안에서 유지되는 커넥션의 최소 크기이다(항상 이 값으로 유지되지 않을 수 있으며 리사이즈 주기에 의해 유지됨).
    maxPool 안에서 유지되는 커넥션의 최대 크기이다.
    act애플리케이션이 사용하고 있는 커넥션의 수이다.
    idle현재 Pool에 들어있는 커넥션의 수이다.
    disp한번 사용하고 버리는 커넥션의 총 개수이다.
    totDB 커넥션의 총 수이다(active + idle + disposable connection).
    wait

    Pool에 커넥션이 비었을 경우 Thread를 기다리게 할 것인지를 결정한다.

    • true: 기다리게 한다.

    • false: Pool과 상관 없는 커넥션을 생성한다.

    work
    • true: DB Pool이 활성화 상태이다.

    • false: 비활성화이거나 아직 생성되지 않은 상태이다.

  • 데이터소스별로 현재 커넥션의 상태를 파악

    dsconinfo 명령을 사용하여 각각의 데이터소스별로 현재 커넥션의 상태를 파악하거나 관련 통계를 볼 수 있다. 명령은 데이터소스의 이름을 인자로 받고 해당 Pool에 있는 커넥션들의 정보를 출력한다.

    다음의 옵션을 설정할 수 있다.

    항목설명
    -k

    몇 번 정보를 출력할지 결정한다.

    (예: dsconinfo -k 20)

    -i

    몇 초에 한 번씩 정보를 출력할지 결정한다. 인자값의 단위는 초(sec)이다.

    (예: dsconinfo -i 1)

    johan>dsconinfo -con johan_container1 datasource1
    ====================================================================
    Connection information list for the engine container[johan_container1]
    
    --------------------------------------------------------------------
    |             id | state | state-time(s) | use-count |   type |
    --------------------------------------------------------------------
    | datasource1-1  | idle  | 48.613        | 0         | pooled |
    | datasource1-2  | idle  | 48.612        | 0         | pooled |
    | datasource1-3  | idle  | 48.611        | 0         | pooled |
    | datasource1-4  | idle  | 48.61         | 0         | pooled |
    | datasource1-5  | idle  | 48.609        | 0         | pooled |
    | datasource1-6  | idle  | 48.608        | 0         | pooled |
    | datasource1-7  | idle  | 48.607        | 0         | pooled |
    | datasource1-8  | idle  | 48.606        | 0         | pooled |
    | datasource1-9  | idle  | 48.606        | 0         | pooled |
    | datasource1-10 | idle  | 48.605        | 0         | pooled |
    --------------------------------------------------------------------
    ====================================================================

    다음은 명령어 실행 결과의 출력되는 항목에 대한 설명이다.

    항목설명
    id해당 컨테이너의 데이터소스 내에서 각 커넥션별로 붙인 고유한 값이다.
    state커넥션의 상태를 나타내며 active와 idle로 나뉜다. active일 경우 현재 사용 중인 커넥션을 의미한다.
    state time

    커넥션이 현재 상태로 바뀐 후 지속된 시간을 의미한다.

    예를 들어 'datasource1-1' 커넥션의 경우 유휴(idle) 상태로 약 48초간 있었음을 알 수 있다.

    usecountopen, close 짝이 몇 번 발생했는지를 의미한다.
    typePooling된 커넥션인지 disposable 커넥션인지 구분할 수 있다.

  • 커넥션을 가장 마지막에 사용한 스레드 조회

    -t 옵션을 사용하면 커넥션을 가장 마지막에 사용한 스레드의 이름을 알 수 있다.

    johan>dsconinfo -con johan_container1 -t datasource1
    ==============================================================================
    Connection information list for the engine container[johan_container1]
    
    ------------------------------------------------------------------------------
    |            id |  state | state-time(ms) | use-count |   type | thread name |
    ------------------------------------------------------------------------------
    | datasource1-1 |   idle |          10000 |         3 | pooled |             |
    ------------------------------------------------------------------------------
    | datasource1-2 | active |           1300 |         2 | pooled |     http-w1 |
    ------------------------------------------------------------------------------
    ==============================================================================

    위 테이블에서처럼 상태가 active인 경우에 thread name 정보가 출력된다. datasource1-2 커넥션은 Leak일 수도 있으므로 http-w1 스레드는 가장 최근에 커넥션을 사용한 스레드이다.

  • 하나의 커넥션에 대한 정보 조회

    -id 옵션을 통해 하나의 커넥션에 대한 정보를 볼 수 있다. -id 옵션에 쓰이는 인자는 dsconinfo 명령어를 통해 나타난 커넥션 목록에서 나타난 ID이다.

    johan>dsconinfo -con johan_container1 -id datasource1-1 datasource1
    ===============================================
    Connection[datasource1-1] information in the engine container[johan_container1]
    
    -----------------------------------------------
    | state | state-time(ms) | use-count |   type |
    -----------------------------------------------
    |  idle |        506.617 |         0 | pooled |
    -----------------------------------------------
    ===============================================

    예를 들어 datasource1-1 커넥션 ID를 인자로 설정했을 때 state, use count, type 정보가 출력된다. 만약 데이터소스에 getConnection Trace 기능을 사용한다면 이 커넥션을 가져간 애플리케이션들의 정보를 getConnection할 때의 Stack Dump로 확인할 수 있다.

    그리고 SQL Trace를 사용한다면 해당 커넥션에서 실행중인 SQL이 무엇인지 확인할 수 있다. 그리고 이때도 -t 옵션이 적용된다.

8.5. JEUS JDBC 프로그래밍

구성한 Connection Pool을 사용하여 코드를 작성하는 방법을 설명한다. 본 절은 JDBC 애플리케이션 프로그래밍 가이드로 활용하기 바란다.

8.5.1. 간단한 JDBC 프로그래밍

다음의 코드는 JEUSMain.xml에 구성된 데이터소스를 사용하여 커넥션을 얻는 방법에 대해 보여주고 있다.

Context ctx = new InitialContext();
DataSource ds = (DataSource) ctx.lookup("ds1");
Connection con = ds.getConnection();
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery("select * from test");
...
...
con.close();

경고

커넥션을 사용한 후에는 반드시 닫아야 한다.

8.5.2. 트랜잭션 프로그래밍 규칙

JEUS는 트랜잭션 프로그래밍을 위한 표준 단계를 정의한다. UserTransaction를 사용하는 모든 애플리케이션은 표준 단계들을 따라야 한다.

  1. 트랜잭션을 시작한다.

  2. DB 커넥션을 얻어 온다.

  3. 트랜잭션의 나머지를 코딩한다.

    경고

    만약 다른 트랜잭션 처리를 원한다면 반드시 새로운 커넥션을 다시 얻어야 한다.

8.5.3. JDBC 드라이버의 커넥션 인스턴스를 얻고 싶을 때

JEUS에서는 애플리케이션에 커넥션을 넘겨줄 때 커넥션의 상태 관리를 위하여 래퍼를 사용하게 된다. 그러나 Oracle의 CLOB, XMLType 등은 해당 JDBC 드라이버의 커넥션 인스턴스를 직접적으로 필요로 하기 때문에 드라이버 내부적으로 클래스 캐스팅을 하다가 ClassCastException이 발생한다. 이는 JEUS뿐만 아니라 대부분의 WAS 제품에서 발생할 수 있는 문제이며, 애플리케이션에서는 WAS에서 넘어오는 커넥션이 드라이버의 커넥션 인스턴스일 것이라고 가정하고 구현해서는 안 된다. 하지만 이를 우회적으로 피할 수 있는 방법이 있다.

애플리케이션에서는 다음과 같은 방법으로 드라이버의 커넥션 인스턴스를 얻을 수 있다.

import java.sql.Connection;
import java.sql.DatabaseMetaData;

....

Connection conn = datasource.getConnection();
DatabaseMetaData metaData = conn.getMetaData();

OracleConnection oraConn = (OracleConnection)metaData.getConnection();

8.5.4. Standalone 클라이언트에서의 Connection Pool

Standalone 클라이언트에서 clientcontainer.jar 또는 jclient.jar를 이용해서 JEUS에 등록된 데이터소스 정보를 이용할 수 있다. 클라이언트에서 JEUSMain.xml에 등록한 JNDI 이름으로 데이터소스를 찾으면 해당 정보를 클라이언트로 가져와서 로컬 JVM에 Connection Pool을 구성하도록 되어 있다.

이러한 방식은 클라이언트 프로그램에서 JNDI lookup 방식으로 편리하게 데이터베이스에 접근할 수 있다는 장점이 있다. 하지만 데이터베이스 입장에서는 WAS 이외에 JDBC 드라이버로 접근하는 클라이언트가 늘어나게 되는 것이고, 클라이언트 프로그램이 서버처럼 계속 돌아가는 프로그램이 아니라면 Connection Pool을 구성하는 것은 불필요한 자원 낭비라고 할 수 있다. 따라서 실제 서비스를 구현할 때는 이 방식을 사용하는 것을 권장하지 않는다.

경고

JEUS 서버에 구성된 Connection Pool에서 커넥션을 가져와서 사용하는 것이 아니다. 만약 그렇게 하고 싶다면 커넥션을 사용하는 로직을 EJB로 구현해서 JEUS에 디플로이하고 EJB를 찾아서(lookup) 사용해야 한다.

8.6. 글로벌 트랜잭션(XA)과 커넥션 공유

커넥션 공유(connection sharing)은 같은 트랜잭션 내에서는 하나의 리소스에 대해 항상 하나의 커넥션만 사용하는 것을 보장한다는 개념으로, JCA 표준에 언급되어 있다. JDBC Connection Pool 입장에서의 리소스는 대부분 데이터베이스(또는 데이터소스)이다. JEUS의 DB Connection Pool에서는 특별한 설정이 필요없이 기본적으로 XA 데이터소스에 대해 커넥션 공유를 제공하며 로컬 XA 데이터소스도 마찬가지로 제공한다.

ProFrame과 같은 애플리케이션 프레임워크에서는 같은 트랜잭션 내에서 하나의 데이터소스에 여러 번 getConnectionclose를 반복하게 되는 경우가 많으므로 커넥션 공유를 사용하는 것이 바람직하다. 그렇지 않으면 하나의 DB에 대해 여러 개의 물리적 커넥션이 트랜잭션에 참여하기 때문에 DB 입장에서는 불필요하게 많은 트랜잭션 Lock을 필요로 하게 되고 이는 트랜잭션 성능에 큰 영향을 미칠 수 있다.

만약 커넥션 공유를 사용하고 싶지 않을 때는 XA 데이터소스를 사용하는 Java EE 컴포넌트 설정(web.xml, ejb-jar.xml 등)에 다음과 같이 설정할 수 있다. 참고로 <res-ref-name>에는 보통 JEUSMain.xml에 설정된 데이터소스의 JNDI 이름을 입력한다.

<resource-ref>
    <res-ref-name>jdbc/xads</res-ref-name>
    <res-type>javax.sql.DataSource</res-type>
    <res-sharing-scope>Unshareable</res-sharing-scope>
</resource-ref>

아무 설정을 하지 않으면 기본적으로 <res-sharing-scope>가 Shareable이기 때문에 서블릿이나 EJB 컴포넌트에서 항상 커넥션 공유를 사용한다. 단, 로컬 XA 데이터소스는 항상 커넥션을 공유해야 하기 때문에 Unshareable로 설정하면 에러(java.sql.SQLException)가 발생하도록 되어 있다.

8.7. 복수 사용자에 대한 Connection Pooling 서비스 지원

JEUS는 데이터소스 설정상 명시된 기본 사용자에 대해서만 Connection Pooling 서비스를 지원해왔다. 만약 기본 사용자가 아닌 사용자 정보로 커넥션을 요청할 경우에는 해당 커넥션을 무조건 일회용(disposable) 커넥션으로 다룬다. 일회용 커넥션은 이름 그대로 커넥션 요청이 있을 때마다 생성됐다가 사용 후 바로 Connection Pool로부터 제거되므로 그에 대한 요청이 많을 경우 Connection Pooling 서비스에는 상당한 부담이 될 수 있다.

이를 위해 JEUS v6.0 Fix#9부터는 복수 사용자에 대하여 Connection Pooling 서비스를 지원한다. 이는 하나의 Connection Pool이 외부에는 투명하게 사용자별로 다시 나뉘어 관리되는 방식이므로 클라이언트는 기존의 방식과 비교하여 아무런 차이 없이 Connection Pooling 서비스를 이용할 수 있다. 즉, Connection Pool에 존재하는 커넥션들은 서로 사용자 정보가 달라도 Connection Pool의 전체적인 설정과 운영 정책은 공통적으로 따르게 되는 것이다. 그러므로 다수의 사용자별 커넥션을 빈번하게 사용하게 되는 경우에도 일회용 커넥션을 생성해야 하는 부담 없이 보다 효율적으로 Connection Pooling 서비스를 이용할 수 있다.