[JSP] 커넥션 풀

커넥션 풀(Connection Pool)


1. 커넥션 풀(DBCP)

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

웹 프로그램에서는 데이터베이스의 환경설정과 연결 관리 등을 따로 XML파일이나 속성 파일을 사용해서 관리하고, 이렇게 설정된 정보를 이름을 사용하여 획득하는 방법을 사용한다.

- 웹 컨테이너가 실행되면서 커넥션(connection) 객체를 미리 풀(pool)에 생성해 둡니다.

- DB와 연결된 커넥션(connection)을 미리 생성해서 풀(pool) 속에 저장해 두고 있다가 필요할 때에 가져다 쓰고 반환한다.

- 미리 생성해두기 때문에 데이터베이스에 부하를 줄이고 유동적으로 연결을 관리 할 수 있다.

이렇게 풀 속에 미리 생성되어 있는 커넥션을 가져다가 사용하고, 사용이 끝나면 커넥션을 풀에 반환한다.


2. 커넥션풀(DBCP) 사용 이유


만약 한명의 접속자가 웹 사이트에 접속했다고 가정한다. 해당 웹 사이트에서 접속자는 게시판을 확인하고 자신이 쓴 게시물을 수정하고 또 새로운 게시글을 등록한다고 가정해보자.

그럼 이 한명의 접속자로 인해 DB접속은 아래와 같이 발생한다.

1) 데이터 취득

2) 검색 후 데이터 취득

3) 데이터 갱신

4) 데이터 새등록


즉 한명의 접속자로 인해 단 시간에 4번의 DB 접속이 일어난다.

그럼 웹상에서 아주 짧은 시간에 몇번의 DB 접속이 일어날까? 만약 접속자가 1000명 이라면? 즉 커넥션 풀이란 미리 커넥션 객체를 생성하고 해당 커넥션 객체를 관리하는것을 의미한다.

즉 '커넥션 풀에 DB와 연결을 해 놓은 객체를 두고 필요할 때마다 커넥션 풀에서 빌려온다' 라고 이해하면 개념잡기에 쉬울듯 하다.

그리고 연결이 끝나면 다시 풀에 돌려준다.

커넥션 풀을 너무 크게 해놓으면 당연히 메모리 소모가 클것이고, 적게 해놓으면 커넥션이 많이 발생할 경우 대기시간이 발생할 것이다. 즉 웹 사이트 동시 접속자수 등 서버 부하에 따라 크기를 조정해야 할것이다.


cp.) 

서버는 동시에 사용할 수 있는 사람의 수라는 개념이 존재합니다.일반적인 커넥션을 이용하면 동시 접속자 수를 벗어나게 될 경우 에러(예외)가 발생하게 됩니다.

예외가 발생하면 그 접속자는 더이상 처리를 하지 못하므로, 사이트 이용자는 다시 접속을 시도해야하는 불편함이 있습니다.이를 해결하기 위해 탄생한 것이 커넥션 풀 입니다.

 커넥션 풀이란 동시 접속자가 가질 수 있는 커넥션을 하나로 모아놓고 관리한다는 개념입니다. 누군가 접속하면 자신이 관리하는 풀에서 남아있는 커넥션을 제공합니다.

하지만 남아있는 커넥션이 없는 경우라면 해당 클라이언트는 대기 상태로 전환시킵니다. 그리고 커넥션이 다시 풀에 들어오면 대기 상태에 있는 클라이언트에게 순서대로 제공합니다.

- JDBC를 통하여 DB에 연결하기 위해서는 드라이버(Driver)를 로드하고 커넥션(connection) 객체를 받아와야 한다.

- JDBC를 사용하면 사용자가 요청을 할 때마다 매번 드라이버를 로드하고 커넥션 객체를 생성하여 연결하고 종료하기 때문에 매우 비효율적이다.

- 이런 문제를 해결하기 위해서 커넥션풀(DBCP)를 사용한다.


3. 커넥션풀(DBCP)의 특징

 - 풀 속에 미리 커넥션이 생성되어 있기 때문에 커넥션을 생성하는 데 드는 연결 시간이 소비되지 않는다.

 - 커넥션을 계속해서 재사용하기 때문에 생성되는 커넥션 수가 많지 않다.

--> 커넥션 풀을 사용하면 커넥션을 생성하고 닫는 시간이 소모되지 않기 때문에 그만큼 어플리케이션의 실행 속도가 빨라지며, 또한 한 번에 생성될 수 있는 커넥션 수를 제어하기 때문에 동시 접속자 수가 몰려도 웹 어플리케이션이 쉽게 다운되지 않는다.


4. 그렇다면 동시 접속자 처리는..?

커넥션 풀에서 생성되어 있는 커넥션의 갯수는 한정적이다. 그렇다면 동시 접속자가 많아지면 어떻게 될까?

커넥션 풀은 누군자 접속하면 커넥션 풀에 남아 있는 커넥션을 제공하는 식이다. 하지만 남아있는 커넥션이 없을 경우 해당 클라이언트는 대기 상태로 전환이 되고, 커넥션이 반환되면 대기하고 있는 순서대로 커넥션이 제공된다.


5. 환경설정

(1) DBCP 라이브러리 추가

- 이전버전 commons-dbcp-1.4jar, commons-pool-1.6.jar, commons-collections-3.2.1-bin.zip 3개의 라이브러리는 톰캣 6.0 부터 tomcat-dbcp.jar 파일로 하나로 통합되었다.

- 톰캣설치폴더 lib 폴더에 있는 tomcat-dbcp.jar 파일을 웹프로젝트\WebContent\WEB-INF\lib 경로에 복사한다.



(2) DB 라이브러리 추가(오라클 - ojdbc.jar)



(3) \Servers\Tomcat v6.0 Server at localhost-config\context.xml 파일에 Resource 태그 추가


※ 커넥션 풀의 속성

  속성

 설명

 maxActive 

 커넥션 풀이 제공할 최대 커넥션 갯수 

 whenExhaustedAction

 커넥션 풀에서 가져올 수 있는 커넥션이 없을 때 어떻게 동작할지를 지정.

0일 경우 에러 발생

1일 경우 maxWait 속성에서 지정한 시간만큼 커넥션을 구할때까지 기다림.

2일 경우 일시적으로 커넥션을 생성해서 사용

 maxWait

whenExhaustedAction 속성의 값이 1일 때 사용되는 대기 시간.

단위는 1/1000초, 0보다 작을 경우 무한히 대기

 maxIdle 

 사용되지 않고 풀에 저장될 수 있는 최대 커넥션 갯수.

음수일 경우 제한이 없음

 minIdle

사용되지 않고 풀에 저장될 수 있는 최소 커넥션 갯수. 

 testOnBorrow 

 true일 경우 커넥션 풀에서 커넥션을 가져올 때 커넥션이 유효한지의 여부를 검사 

 testOnReturn 

 true일 경우 커넥션 풀에 커넥션을 반환할 때 커넥션이 유효한지의 여부를 검사 

 timeBetweenEvctionRunsMillis 

사용되지 않는 커넥션을 추출하는 쓰레드의 실행 주기 지정.

양수가 아닐 경우 실행되지 않는다.

시간 단위는 1/1000초. 

 numTestsPerEvictionRun 

사용되지 않는 커넥션을 몇 개 검사할 지 지정 

 minEvictableIdleTimeMillis 

사용되지 않는 커넥션을 추출할 때 이 속석에서 지정한 시간 이상 비활성화 상태인 커넥션만 추출.

양수가 아닌 경우 비활성화된 시간으로는 풀에서 제거되지 않음.

시간 단위는 1/1000초 

 testWhileIdle

 true일 경우 비활성화 커넥션을 추출할 때 커넥션이 유효한지의 여부를 검사해서 유효하지 않은 커넥션은 풀에서 제거.



(4) 웹프로젝트\WebContent\WEB-INF\web.xml 파일에 <resource-ref> 태그 추가

- <res-ref-name> 태그의 이름은 contex.xml 파일 <resource>태그의 name 속성의 이름과 같아야한다.

- (4) 작업은 톰캣 6.0 이상부터는 생략 가능하다.



(5) JAVA 코드로 연결

- 서블릿에서 작성한다. (MVC 구조로 구현할 경우)

- 예외를 가지고 있으므로 예외처리를 해준다.

import java.sql.Connection;

import javax.naming.Context;

import javax.naming.InitialContext;

import javax.sql.DataSource;

 

public class DBCP {

    /*

     * 이 코드를 아래와 같이 줄여서 작성가능하다.

    Context initContext = new InitialContext();

    Context envContext  = (Context) initContext.lookup("java:/comp/env");

    // ("java:comp/env"): JNDI 서비스에 접근하기 위한 기본 이름(이 자원을 찾겠다.--> web.xml의 <res-ref-name>

    DataSource dataSource = (DataSource) envContext.lookup("jdbc/oracle");

    Connection conn = dataSource.getConnection();

    */

     

    Context context = new InitialContext();

    DataSource dataSource = (DataSource) context.lookup("java:comp/env/jdbc/oracle");

    Connection con = dataSource.getConnection();        

}



(6) servers > context.xml 파일 복사

- 서버가 변경되거나 서버에서 프로젝트를 제거하는 경우에 다시 추가해서 실행하는 번거로움이 있다.

- \Servers\Tomcat v6.0 Server at localhost-config\context.xml 파일을 복사한다.

- 웹프로젝트\WebContent\WEB-INF\ 디렉터리에 붙여넣기 한다.


6. JNDI(Java Naming and Directory Interface) 

설정된 정보를 이름으로 획득하려면 자바의 네이밍 API를 사용해야 한다. 네이밍 패키지의 클래스를 가지고 이름으로 객체를 획득하는 것을 JNDI(Java Naming and Directory Interface)라고 한다.

- 서비스가 다른 서비스를 탐색할 때 유용하게 사용된다. (분산된 자원)

- 분산된 자원 끼리의 탐색을 원할하게 하기 위한 type casting 임. (DNS 도 이에 속함) 

- JNDI에 설정해 놓았다는것은 Context.xml에 리소스를 생성해놓은 것을 말한다.

cf.) 

이름을 이용해서 원하는 정보 혹은 자원(Connection)을 찾을 수 있는 서비스

javax.naming 서비스

initialContext 클래스


(1) META-INF/context.xml

리소스 등록하는 설정파일

<Context> 

<!-- Resource를 등록하여 웹에서 JNDI로 호출할 이름과 정보를 설정한다. -->

   <Resource name="jdbc/myconn" auth="Container" type="javax.sql.DataSource"

   factory="org.apache.tomcat.dbcp.dbcp.BasicDataSourceFactory"

   driverClassName="org.gjt.mm.mysql.Driver"

   url="jdbc:mysql://localhost:3306/web_java?autoReconnect=true"

   username="root" password="12345678" 

   maxActive="100" maxIdle="30" maxWait="10000"

   removeAbandoned="true" removeAbandonedTimeout="60"/> 

   <!-- 

1. name : JNDI로 호출될 이름을 설정한다. (접근 -> java:comp/env/jdbc/myconn)

   2. auth : DBCP를 관리할 관리자 (Container or Application)

   3. type : 해당 resource의 return type 

      (DataSource는 Connection 객체를 반환할 수 있다.)

   4. factory : dbcp를 유용하는 관리 클래스 (Tomcat 5.x에 기본으로 존재하는 클래스)

      (직접 DBCP 클래스를 지정해도 동작하는데 문제가 없다.)

      (그러나, Factory 클래스를 이용하면 좀더 안정적으로 관리할 수 있다.)

   5. driverClassName : JDBC를 이용하기 위한 드라이버 클래스

   6. url : DB의 접속 URL (속성으로 자동 재 접속을 선택했다.)

   7. username : DB의 계정 명

   8. password : 계정에 대한 비밀번호

   9. maxActive : 최대 접속 허용 개수

   10. maxIdle : DB Pool에 여분으로 남겨질 최대 Connection 개수

   11. maxWait : DB 연결이 반환되는 Timeout의 최대 시간 (-1은 무한 대기)

   12. removeAbandoned : Connection이 잘못 관리되어 버려진 연결을 찾아 재활용할 것인지의 여부 설정

       (true 설정일 때 현재 DB 연결이 적으면 버려진 연결을 찾아 재활용)

   13. removeAbandonedTimeout : 버려진 연결로 인식할 기본 시간 설정

       (초 단위로 해당 시간이 지나면 버려진 연결로 인식한다.)

   -->   

</Context>


(2) WEB-INF/web.xml
<servlet-mapping>
<servlet-name>My_06</servlet-name>
<url-pattern>/Servlet_06</url-pattern>
</servlet-mapping>
<resource-ref>
<description>My SQL Resource</description><!-- 리소스 설명 -->
<res-ref-name>jdbc/myconn</res-ref-name><!-- 리소스 이름(JNDI명) -->
<res-type>javax.sql.DataSource</res-type><!-- 리턴 Type -->
<res-auth>Container</res-auth><!-- 관리 계층 -->
</resource-ref>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>


(3) 자바코드

import java.io.*;

import javax.servlet.*;

import javax.servlet.http.*;

import java.sql.*;

import javax.sql.*;//DataSource 클래스를 위해 사용

import javax.naming.*;//JNDI를 위해 사용


public class Round16_06_Servlet extends HttpServlet {

public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {

request.setCharacterEncoding("euc-kr");

String subject = request.getParameter("subject");

String author = request.getParameter("author");

String contents = request.getParameter("contents");

response.setContentType("text/html;charset=euc-kr");

PrintWriter out = response.getWriter();

out.println("<html><body><center><h3>");

Connection conn = null;

PreparedStatement pstmt = null;

String query = "insert into Round16_Table_01 values (null, ?, ?, ?)";

try {

Context context = new InitialContext();

//JNDI를 이용하기 위한 객체 생성

DataSource source = (DataSource)context.lookup("java:comp/env/jdbc/myconn");

  // lookup(): 등록된 naming 서비스로부터 자원을 찾고자할 때 사용하는 메서드

//context 객체를 통해 이름으로 Resource를 획득한다. 

  //("jdbc/myconn"): JNDI 서비스에 접근하기 위한 기본 이름(이 자원을 찾겠다.--> web.xml의 <res-ref-name>

//JNDI의 모든 이름은 기본적으로 java:comp/env에 등록되어 있다.

//해당 영역에서 jdbc/myconn으로 설정된 이름을 획득한다.

conn = source.getConnection();

//source로 부터 Connection 객체를 획득한다. 

//이 객체는 이제 Container의 DBCP에 의해 관리된다. 

}catch(Exception e) {}

try {

pstmt = conn.prepareStatement(query);

pstmt.setString(1, subject);

pstmt.setString(2, author);

pstmt.setString(3, contents);

int res = pstmt.executeUpdate();

if(res > 0)

out.println("Success Save!!");

pstmt.close();

conn.close();

}catch(Exception e) {

out.println("SQL Process Error : " + e.getMessage());

}

out.println("</h3></center></body></html>");

}

}



'Programing > JSP/Servlet' 카테고리의 다른 글

[JSP] HTTP 헤더  (0) 2014.12.25
[Servlet] 서블릿 이벤트  (0) 2014.12.18
[Servlet] 서블릿 필터  (0) 2014.12.10
[Servlet] 서블릿 기초  (0) 2014.12.10
[Servlet] 데이터 저장 영역  (0) 2014.12.10