개발/JSP

DBCP를 이용해서 커넥션 풀 사용하기

까망군 2011. 10. 27. 12:57
자카르타 프로젝트의 DBCP API를 사용할 때에는 다음과 같은 과정을 거친다.

1. DBCP 관련 Jar 파일 및 JDBC 드라이버 Jar 파일 설치하기
2. 커넥션 풀 관련 설정 파일 초기화하기
3. 커넥션 풀 관련 드라이버 로딩하기
4. 커넥션 풀로부터 커넥션 사용하기

(1) 필요한 jar 파일 복사하기
  • Commons-DBCP API 관련 Jar 파일 
  • Commons-DBCP API가 사용하는 Commons-Pool API의 Jar 파일(1.3 이상)
이 두 라이브러리는 http://commons.apache.org 사이트에서 다운로드 받을 수 있다.

다운받은 라이브러리의 압축을 풀면 jar 파일을 발견할 수 있는데, 이 두 jar 파일을 WEB-INF\lib 디렉터리에 복사해 준다.


(2) 설정 파일 작성 및 위치

다음으로 할 작업은 커넥션 풀 설정 파일을 작성하는 것이다. 다음과 같은 경로에 커넥션 풀 설정 파일을 작성한다.

chap12\WEB-INF\classes\pool.jocl
<object class="org.apache.commons.dbcp.PoolableConnectionFactory" 
		xmlns="http://apache.org/xml/xmlns/jakarta/commons/jocl">

	<object class="org.apache.commons.dbcp.DriverManagerConnectionFactory">
		<string value="jdbc:mysql://localhost:3306/chap12?useUnicode=true&amp;characterEncoding=euckr"/>
		<string value="jernin20"/>
		<string value="qowhdrnr"/>
	</object>
	
	<object class="org.apache.commons.pool.impl.GenericObjectPool">
		<object class="org.apache.commons.pool.PoolableObjectFactory" null="true" />
	</object>
	
	<object class="org.apache.commons.pool.KeyedObjectPoolFactory" null="true"/>
	
	<string null="true"/>
	
	<boolean value="false"/>
	
	<boolean value="true"/>
</object>

(3) 커넥션 풀 초기화하기

DBCP API를 통해서 커넥션 풀을 사용하기 위해서는 커넥션 풀과 관련된 JDBC 드라이버를 로딩해 주어야 한다. DBCP API를 사용할 때에 로딩해 주어야 할 JDBC 드라이버는 다음과 같다

  • DBMS에 연결할 때 사용될 JDBC 드라이버
  • org.apache.commons.dbcp.PoolingDriver : DBCP API의 JDBC 드라이버

chap12\WEB-INF\src\kame\jdbc\loader\DBCPInit.java
package kame.jdbc.loader;

import javax.servlet.http.HttpServlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import java.util.StringTokenizer;

public class DBCPInit extends HttpServlet {

	public void init(ServletConfig config) throws ServletException {
		try {
			String drivers = config.getInitParameter("jdbcdriver");
			StringTokenizer st = new StringTokenizer(drivers, ",");
			while (st.hasMoreTokens()) {
				String jdbcDriver = st.nextToken();
				Class.forName(jdbcDriver);
			}
			
			Class.forName("org.apache.commons.dbcp.PoolingDriver");

		} catch(Exception ex) {
			throw new ServletException(ex);
		}
	}
}

소스코드를 작성했다면 다음과 같은 명령어를 이용해서 DBCPInit.java를 컴파일 한다.

D:\..\webapps\chap12\WEB-INF>set CLASSPATH=lib\commons-dbcp-1.2.2.jar;d:\tomcat\apache-tomcat-6.0.18\lib\servlet-api.jar

D:\..\webapps\chap12\WEB-INF>javac -d classes src\kame\jdbc\loader\DBCPInit.java

D:\..\webapps\chap12\WEB-INF>javac -d classes src\kame\jdbc\loader\Loader.java


Loader 클래스를 사용할 때와 마찬가지로 WEB-INF\web.xml 파일에 DBCPInit 서블릿 클래스에 대한 설정 정보를 추가하여 웹 어플리케이션이 시작될 때 DBCPInit 서블릿 클래스가 시작되도록 한다.
<servlet>
	<servlet-name>DBCPInit</servlet-name>
	<servlet-class>kame.jdbc.loader.DBCPInit</servlet-class>
	<init-param>
		<param-name>jdbcdriver</param-name>
		<param-value>com.mysql.jdbc.Driver</param-value>
	</init-param>
	<load-on-startup>1</load-on-startup>
</servlet>

(4) 커넥션 풀로부터 커넥션 사용하기

커넥션 풀을 사용하기 위한 JDBC 드라이버 및 DBMS에 연결할 때 사용할 JDBC 드라이버를 로딩하면 커넥션 풀로부터 커넥션을 가져와 사용할 수 있다. DBCP 기반의 커넥션 풀로부터 커넥션을 가져오는 코드는 다음과 같다.

chap12\viewMemberUsingPool.jsp
<%@ page contentType = "text/html; charset=euc-kr" %>
<%@ page import = "java.sql.DriverManager" %>
<%@ page import = "java.sql.Connection" %>
<%@ page import = "java.sql.Statement" %>
<%@ page import = "java.sql.ResultSet" %>
<%@ page import = "java.sql.SQLException" %>
<html>
<head><title>회원 목록</title></head>
<body>

MEMBMER 테이블의 내용
<table width="100%" border="1">
<tr>
    <td>이름</td><td>아이디</td><td>이메일</td>
</tr>
<%
    
    Connection conn = null;
    Statement stmt = null;
    ResultSet rs = null;
    
    try {
        String jdbcDriver = "jdbc:apache:commons:dbcp:/pool";
        String query = "select * from MEMBER order by MEMBERID";
        conn = DriverManager.getConnection(jdbcDriver);
        stmt = conn.createStatement();
        rs = stmt.executeQuery(query);
        while(rs.next()) {
%>
<tr>
    <td><%= rs.getString("NAME") %></td>
    <td><%= rs.getString("MEMBERID") %></td>
    <td><%= rs.getString("EMAIL") %></td>
</tr>
<%
        }
    } finally {
        if (rs != null) try { rs.close(); } catch(SQLException ex) {}
        if (stmt != null) try { stmt.close(); } catch(SQLException ex) {}
        if (conn != null) try { conn.close(); } catch(SQLException ex) {}
    }
%>
</table>

</body>
</html>

결과 화면

* 커넥션 풀에서 구한 Connection의 close() 메서드를 호출하면, 커넥션이 닫히는 것이 아니라 커넥션 풀로 반환된다. 이렇게 커넥션 풀에 커넥션을 반환하는 메서드를 close()로 지정한 이유는 기존의 코드를 최소한으로 변경하는 범위 내에서 커넥션 풀을 사용할 수 있도록 하기 위함이다. 물론, JDBC 프로그래밍 코딩 형태를 동일하기 유지하기 위한 것도 close()를 사용하는 이유이다.

(5) 커넥션 풀 속성

chap12\WEB-INF\classes\sample.jocl
<object class="org.apache.commons.dbcp.PoolableConnectionFactory" 
		xmlns="http://apache.org/xml/xmlns/jakarta/commons/jocl">

	<object class="org.apache.commons.dbcp.DriverManagerConnectionFactory">
		<string value="jdbc:mysql://localhost:3306/chap12?useUnicode=true&amp;characterEncoding=euckr"/>
		<string value="jernin20"/>
		<string value="qowhdrnr"/>
	</object>
	
	<object class="org.apache.commons.pool.impl.GenericObjectPool">
		<object class="org.apache.commons.pool.PoolableObjectFactory" null="true" />
		<int vlaue="10" />	<!-- maxActive -->
		<byte value="1" />	<!-- whenExhaustedAction -->
		<long value="10000" />	<!-- maxWait -->
		<int value="10" />	<!-- maxIdle -->
		<int value="3" />	<!-- minIdle -->
		<boolean value="true" />	<!-- testOnBorrow -->\
		<boolean value="true" />	<!-- testOnReturn -->
		<long value="600000" />	<!-- timeBetweenEvctionRunsMills -->
		<int value="5" />	<!-- numTestsPerEvictionRun -->
		<long value="360000" />	<!-- minEvictableIdleTimeMills -->
		<boolean value="true" />	<!-- testWhileidle -->
	</object>
	
	<object class="org.apache.commons.pool.KeyedObjectPoolFactory" null="true"/>
	
	<string null="true"/>
	
	<boolean value="false"/>
	
	<boolean value="true"/>
</object>

각각의 속성에 들어오는 값은 다음과 같다.

 속성  설명
 maxActive 커넥션 풀이 제공할 최대 커넥션 개수 
 whenExhaustedAction 커넥션 풀에서 가져올 수 있는 커넥션이 없을 때 어떻게 동작할지를 지정한다. 1일 경우 maxWait 속성에서 지정한 시간만큼 커넥션을 구할 때까지 기다리며, 0일 경우 에러를 발생시킨다. 2일 경우에는 일시적으로 커넥션을 생성해서 사용한다. 
 maxWait whenExhaustedAction 속성의 값이 1일 때 사용되는 대기 시간. 단위는 1/1000초이며, 0보다 작을 경우 무한히 대기한다. 
 maxIdle 사용되지 않고 풀에 저장될 수 있는 최대 커넥션 개수. 음수일 경우 제한이 없다. 
 minIdle  사용되지 않고 풀에 저장될 수 있는 최소 커넥션 개수 
 testOnBorrow true일 경우 커넥션 풀에 커넥션을 가져올 때 커넥션이 유효한지의 여부를 검사한다. 
 testOnReturn true일 경우 커넥션 풀에 커넥션을 반환할 때 커넥션이 유효한지의 여부를 검사한다. 
 testBetweenEvictionRunsMills 사용되지 않은 커넥션을 추출하는 쓰레드의 실행 주기를 지정한다. 양수가 아닐 경우 실행되지 않는다. 단위는 1/1000초이다. 
 numTestsPerEvictionRun  사용되지 않는 커넥션을 몇 개 검사할지 지정한다.
 minEvictableIdleTimeMills 사용되지 않는 커넥션을 추출할 때 이 속성에서 지정한 시간 이상 비활성화 상태인 커넥션만 추출한다. 양수가 아닌 경우 비활성화된 시간으로는 풀에서 제거되지 않는다. 시간 단위는 1/1000초이다. 
 testWhileIdle true일 경우 비활성화 커넥션을 추출할 때 커넥션이 유효한지의 여부를 검사해서 유효하지 않은 커넥션은 풀에서 제거한다. 

몇몇 속성은 성능에 중요한 영향을 미치기 때문에 웹 어플리케이션의 사용량에 따라서 알맞게 지정해 주어야 하는데, 다음과 같이 고려해서 각 속성의 값을 지정하는 것이 좋다.

maxActive : 사이트의 최대 커넥션 사용량을 기준으로 지정. 동시 접속자수에 따라서 지정한다.
minIdle : 사용되지 않는 커넥션의 최소 개수를 0으로 지정하게 되면 풀에 저장된 커넥션의 개수가 0이 될 수 있으며, 이 경우 커넥션이 필요할 때 다시 커넥션을 생성하게 된다. 따라서 커넥션의 최소 개수는 5개 정도로 지정해두는 것이 좋다.
timeBetweenEvictionRunMills : 이 값을 알맞게 지정해서 사용되지 않는 커넥션을 풀에서 제거하는 것이 좋다. 커네션의 동시 사용량은 보통 새벽에 최저이며 낮 시간대에 최대에 이르게 되는데 이 두 시간대에 필요한 커넥션의 개수 차이는 수십 개에 이르게 된다. 이때 최대 상태에 접어들었다가 최소 상태로 가게 되면 풀에서 사용되지 않는 커넥션의 개수가 점차 증가하게 된다. 따라서 사용되지 않는 커넥션은 일정 시간 후에 삭제되도록 하는 것이 좋다. 보통 10~20분 단위로 사용되지 않는 커넥션을 검사하도록 지정하는 것이 좋다.
testWhileIdle : 사용되지 않는 커넥션을 검사할 때 유효하지 않은 커넥션은 검사하는 것이 좋다.