개발

[JSP] 커넥션 풀

Handy Smurf 2020. 7. 26. 22:59

 

커넥션 풀 기법이란 데이터 베이스와 연결된 커넥션을 미리 만들어 pool 풀 속에 저장해 두고 있다가 필요할 대에 커넥션을 풀에서 가져다 쓰고 다시 풀에 반환하는 기법을 말한다.

 

  1. 풀에서 커넥션을 가져온다
  2. 커넥션을 사용한다.
  3. 커넥션을 풀에 반환한다.

풀속에 데이터 베이스와 연결된 커넥션을 미리 생성해 놓는다.

데이터베이스 커넥션이 필요하면 커넥션을 새로 생성하는 것이 아니라 풀 속에 미리 생성된 커넥션을 가져다 사용하고, 사용이 끝나면 커넥션을 풀에 반환한다. 풀에 반환된 커넥션은 다음에 다시 사용이 된다.

 

  • 풀 속에 미리 커넥션이 생성되어 있기 때문에 커넥션을 생성하는 데 드는 연결 시간을 줄일 수 있다.
  • 커넥션을 계속해서 재사용하기 때문에 생성되는 커넥션 수가 일정하게 유지된다.

커넥션을 생성하고 닫는데 필요한 시간이 소모되지 않기 때문에 그만큼 어플리케이션의 실행 속도가 빨라진다.

또한 한 번에 생성될 수 있는 커넥션 수를 제어하기 때문에 동시 접속자가 몰려도 웹 어플리케이션이 쉽게 다운되지 않는다.

커넥션 풀을 사용하면 전체적인 웹 어플리케이션의 성능과 처리량이 향상되므로 많은 어플리케이션이 커넥션 풀을 기본으로 사용하고 있다.

 

다양한 커넥션 풀 라이브러리가 존재하는데 오픈 소스 프로젝트인 DBCP API를 이용하여 커넥션 풀을 제공하는 방법을 살펴보자

 

자카르타 프로젝트의 DBCP2 API를 사용할 때에는 다음과 같은 과정을 거치면 된다.

 

  1. DBCP 관련 jar 파일과 JDBC 드라이버 jar 파일 설치하기
  2. 커넥션 풀 초기화 하기
  3. 커넥션 풀로 커넥션 사용하기

필요한 jar 파일 복사하기 

 

  • Commons DBCP API 관련 jar 파일 다운
  • Commons DBCP API가 사용하는 Commons Pool API의 jar 파일
  • 로그 기록에 사용하는 Commons Logging API 관련 jar 파일

 

commons-dbcp2-2.7.0.jar
0.20MB
commons-logging-1.2.jar
0.06MB
commons-pool2-2.8.0.jar
0.12MB
mysql-connector-java-8.0.21.jar
2.29MB

 

 

WEB-INF/lib 디렉토리에 복사한다.

 

 

  1. DBCP 관련 jar 파일과 JDBC 드라이버 jar 파일 설치하기
  2. 커넥션 풀 초기화 하기
  3. 커넥션 풀로 커넥션 사용하기

커넥션풀 초기화 클래스

package jdbc;

import java.sql.DriverManager;
import java.util.Properties;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;

import org.apache.commons.dbcp2.BasicDataSource;
import org.apache.commons.dbcp2.BasicDataSourceFactory;
import org.apache.commons.dbcp2.ConnectionFactory;
import org.apache.commons.dbcp2.DriverManagerConnectionFactory;
import org.apache.commons.dbcp2.PoolableConnection;
import org.apache.commons.dbcp2.PoolableConnectionFactory;
import org.apache.commons.dbcp2.PoolingDriver;
import org.apache.commons.pool2.ObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;

public class DBCPInit extends HttpServlet {

	@Override
	public void init() throws ServletException {
		loadJDBCDriver();
		initConnectionPool();
	}

	private void loadJDBCDriver() {
		try {
			Class.forName("com.mysql.jdbc.Driver");
		} catch (ClassNotFoundException ex) {
			throw new RuntimeException("fail to load JDBC Driver", ex);
		}
	}

	private void initConnectionPool() {
		try {
			String jdbcUrl = 
					"jdbc:mysql://localhost:3305/chap14?" + 
					"useUnicode=true&characterEncoding=utf8&serverTimezone=UTC";
			String username = "root";
			String pw = "1234";

			ConnectionFactory connFactory = 
					new DriverManagerConnectionFactory(jdbcUrl, username, pw);

			PoolableConnectionFactory poolableConnFactory = 
					new PoolableConnectionFactory(connFactory, null);
			poolableConnFactory.setValidationQuery("select 1");

			GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
			poolConfig.setTimeBetweenEvictionRunsMillis(1000L * 60L * 5L);
			poolConfig.setTestWhileIdle(true);
			poolConfig.setMinIdle(4);
			poolConfig.setMaxTotal(50);

			GenericObjectPool<PoolableConnection> connectionPool = 
					new GenericObjectPool<>(poolableConnFactory, poolConfig);
			poolableConnFactory.setPool(connectionPool);
			
			Class.forName("org.apache.commons.dbcp2.PoolingDriver");
			PoolingDriver driver = 
					(PoolingDriver) DriverManager.getDriver("jdbc:apache:commons:dbcp:");
			driver.registerPool("chap14", connectionPool);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
}

 

 

요약

  • 실제 커넥션을 생성할 ConnectionFactory를 생성한다.
  • 커넥션 풀로 사용할 PoolableConnection을 생성하는 PoolableConnectionFactory를 생성한다.
  • 커넥션 풀의 설정정보를 생성한다.
  • 커넥션 풀을 사용할 JDBC 드라이버를 등록한다.

 

커넥션 풀 초기화 서블릿 설정

 

커넥션 풀을 초기화하기 위한 서블릿을 만들었으므로 이제 웹 어플리케이션을 구동할 때 이 서블릿을 실행하도록 설정할 차례이다. 앞서 DriverLoader와 동일하게 web.xml파일에 설정하면 된다.

 

   
    	<servlet>
		<servlet-name>DBCPInit</servlet-name>
		<servlet-class>jdbc.DBCPInit</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>

 

위와 같이 코드를 추가하면 웹 어플리케이션이 시작할 때 DBCPInit 서블릿 클래스가 자동으로 시작되고 init()메서드가 호출된다.

 

 

  1. DBCP 관련 jar 파일과 JDBC 드라이버 jar 파일 설치하기
  2. 커넥션 풀 초기화 하기
  3. 커넥션 풀로 커넥션 사용하기

 

DBCP가 제공하는 PoolingDriver는 커넥션 풀을 위한 JDBC 드라이버이다. 

PoolingDriver를 통해 커넥션 풀로 가져오려면 다음 형식의 JDBC URL을 사용하면 된다.

 

jdbc:apache:commonse:dbcp:풀이름

풀 이름은 PoolingDriver에 커넥션 풀을 등록할 때 지정한다. 예를 들어, 앞서 DBCPInit 클래스를 보면 다음과 같은 코드가 있다.

 

Class.forName("org.apache.commons.dbcp2.PoolingDriver");
			PoolingDriver driver = 
					(PoolingDriver) DriverManager.getDriver("jdbc:apache:commons:dbcp:");
			driver.registerPool("chap14", connectionPool);

이 코드를 보면 커넥션 풀을 등록할 때, 풀의 이름으로 "chap14"를 사용했다. 따라서, 위 커넥션 풀로부터 커넥션을 구하려면 다음과 같은 JDBC URL을 사용하면 된다.

 

Connection conn = null;
....

try {
String jdbcUrl="jdbc:apache:commons:dbcp:chap14";
//커넥션 풀에서 커넥션을 구함
conn = DriverManager.getConnection(jdbcUrl);
...
}finally {

...
//커넥션을 풀에 반환함

if(conn != null) try {conn.close();} catch (SQLException ex){}
}

이 코드를 보면 DBCP API 기반의 커넥션 풀을 사용한다고 해서 특별히 코다가 달라지는 부분이 없다는 것을 알 수 있다. 일반 경우와 마찬가지로 DriverManager.getConnection() 메서드를 사용해서 커넥션을 구해오고, 커넥션을 사용하면 close()메서드로 사용한 커넥션을 닫는다.

 

커넥션 풀에서 구한 Connection의 close()메서드를 호출하면 DB와의 연결을 끊지 않고 해당 커넥션을 풀에 반환한다.

 

실제로 커넥션 풀을 사용하는 완전한 예제이다

앞에서 작성했던 viewMemberUsingPool.jsp를 커넥션 풀을 사용하도록 변경한 것이다.

 

 

<%@ page contentType = "text/html; charset=utf-8" %>
<%@ 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>

MEMBER 테이블의 내용
<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:chap14";
		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>