ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • JDBC 사용 - 커넥션 풀
    Spring/Spring 기본 지식 2020. 3. 23. 17:47
    반응형

    DriverManagerDataSource 사용시 문제점

    설정

    // jdbc 설정하는 xml
    <bean id="PostgreSQLDataSource"  class="org.springframework.jdbc.datasource.DriverManagerDataSource">
       <property name="driverClassName" value="org.postgresql.Driver"/>
       <property name="url" value="jdbc:postgresql://11.111.111.11:1111/test"/> 
       <property name="username" value="postgres"/>
       <property name="password" value="test"/>
     </bean>  

    인위적인 오류 실행 - jdbc 연결을 인위적으로 많이 발생시킴

    ### Cause: org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection; nested exception is org.postgresql.util.PSQLException: The connection attempt failed.]을(를) 발생시켰습니다.
    java.net.SocketTimeoutException: connect timed out
    	at java.net.DualStackPlainSocketImpl.waitForConnect(Native Method)
    	at java.net.DualStackPlainSocketImpl.socketConnect(DualStackPlainSocketImpl.java:85)
    	at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)
    	at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)
    	at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
    	at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:172)
    	at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
    	at java.net.Socket.connect(Socket.java:607)
    	at org.postgresql.core.PGStream.<init>(PGStream.java:70)
    	at org.postgresql.core.v3.ConnectionFactoryImpl.tryConnect(ConnectionFactoryImpl.java:91)
    	at org.postgresql.core.v3.ConnectionFactoryImpl.openConnectionImpl(ConnectionFactoryImpl.java:192)
    	at org.postgresql.core.ConnectionFactory.openConnection(ConnectionFactory.java:49)
    	at org.postgresql.jdbc.PgConnection.<init>(PgConnection.java:195)
    	at org.postgresql.Driver.makeConnection(Driver.java:454)
    	at org.postgresql.Driver.connect(Driver.java:256)
    	at java.sql.DriverManager.getConnection(DriverManager.java:664)
    	at java.sql.DriverManager.getConnection(DriverManager.java:208)
    	at org.springframework.jdbc.datasource.DriverManagerDataSource.getConnectionFromDriverManager(DriverManagerDataSource.java:153)
    	at org.springframework.jdbc.datasource.DriverManagerDataSource.getConnectionFromDriver(DriverManagerDataSource.java:144)
    	at org.springframework.jdbc.datasource.AbstractDriverBasedDataSource.getConnectionFromDriver(AbstractDriverBasedDataSource.java:196)
    	at org.springframework.jdbc.datasource.AbstractDriverBasedDataSource.getConnection(AbstractDriverBasedDataSource.java:159)
    	at org.springframework.jdbc.datasource.DataSourceUtils.doGetConnection(DataSourceUtils.java:111)
    	at org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.java:77)
    	at org.mybatis.spring.transaction.SpringManagedTransaction.openConnection(SpringManagedTransaction.java:84)
    	at org.mybatis.spring.transaction.SpringManagedTransaction.getConnection(SpringManagedTransaction.java:70)
    	at org.apache.ibatis.executor.BaseExecutor.getConnection(BaseExecutor.java:336)
    	at org.apache.ibatis.executor.SimpleExecutor.prepareStatement(SimpleExecutor.java:84)
    	at org.apache.ibatis.executor.SimpleExecutor.doUpdate(SimpleExecutor.java:49)
    	at org.apache.ibatis.executor.BaseExecutor.update(BaseExecutor.java:117)
    	at org.apache.ibatis.executor.CachingExecutor.update(CachingExecutor.java:76)
    	at org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession.java:198)
    	at org.apache.ibatis.session.defaults.DefaultSqlSession.delete(DefaultSqlSession.java:213)
    	at sun.reflect.GeneratedMethodAccessor54.invoke(Unknown Source)
    	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    	at java.lang.reflect.Method.invoke(Method.java:498)
    	at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:434)
    	at com.sun.proxy.$Proxy7.delete(Unknown Source)
    	at org.mybatis.spring.SqlSessionTemplate.delete(SqlSessionTemplate.java:311)
    	at kr.co.noaa.DAO.DroughtPollDAO.deleteReservoirData(DroughtPollDAO.java:27)
    	at kr.co.noaa.service.DroughtPollService.reservoirCheckData(DroughtPollService.java:62)
    	at kr.co.noaa.service.DroughtPollService.DroughtPoll(DroughtPollService.java:51)
    	at kr.co.noaa.HomeController.home(HomeController.java:43)
    	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    	at java.lang.reflect.Method.invoke(Method.java:498)
    	at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:221)
    	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:136)
    	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:114)
    	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827)
    	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738)
    	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
    	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:963)
    	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897)
    	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
    	at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)
    	at javax.servlet.http.HttpServlet.service(HttpServlet.java:634)
    	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
    	at javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
    	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
    	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
    	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)
    	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
    	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:490)
    	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)
    	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
    	at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:678)
    	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
    	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
    	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408)
    	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
    	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:853)
    	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1587)
    	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
    	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    	at java.lang.Thread.run(Thread.java:748)

     

     

    풀링 기능이 있는 데이터 소스 빈과 비교하여 데이터 소스 빈의 한 가지 차이점이 있다면, 설정을 위한 풀링 설정 프로퍼티가 존재하지 않는다는 점이다.

    작은 규모의 애플리케이션과 개발 단계에서는 데이터 소스가 좋은 선택이 되겠지만, 상용 애플리케이션의 사용은 심각하게 고려해야 한다.

    SingleConnectionDataSource는 사용할 수 있는 데이터베이스 커넥션이 오직하나뿐이므로 멀티스레드 애플리케이션에서는 제대로 동작하지 않는다. 또한 DriverManagerDataSource의 경우, 비록 멀티스레드에서도 동작은 하지만 커넥션이 필요한 때마다 새로운 커넥션을 생성하므로 심각한 성능저하를 유발한다.

    이러한 제약 사항때문에 커넥션 풀링 기능이 있는 데이터 소스를 사용할 것을 강력하게 권장한다.

     

     

     

    커넥션 풀 사용(BasicDataSource)

    // pom.xml 에 설정 추가
    <dependency> 
      <groupId>org.apache.commons</groupId> 
      <artifactId>commons-dbcp2</artifactId> 
      <version>2.2.0</version> 
    </dependency>

     

    <bean
        class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations" value="classpath:/jdbc.properties" />
        <property name="fileEncoding" value="UTF-8" />
    </bean>
    
    <!-- JDBC-PostgreSQL -->
    <bean id="PostgreSQLDataSource" class="org.apache.commons.dbcp.BasicDataSource" >
          <property name="driverClassName" value="${jdbc.driverClassName}" />
          <property name="url" 			 value="${jdbc.postgis.url}" />
          <property name="username" 		 value="${jdbc.postgis.username}" />
          <property name="password" 		 value="${jdbc.postgis.password}" />
          <property name="initialSize" 	 value="${jdbc.postgis.initialSize}" />
          <property name="maxActive" 		 value="${jdbc.postgis.maxActive}" />
          <property name="maxIdle" 		 value="${jdbc.postgis.maxIdle}" />
          <property name="minIdle" 		 value="${jdbc.postgis.minIdle}" />
          <property name="maxWait" 		 value="${jdbc.postgis.maxWait}" />
    </bean>

     

    커넥션 풀(DBCP) 특징

    • 웹 컨테이너(WAS)가 실행되면서 connection 객체를 미리 pool에 생성해 둡니다.
    • HTTP 요청에 따라 pool에서 connection객체를 가져다 쓰고 반환한다.
    • 이와 같은 방식으로 물리적인 데이터베이스 connection(연결) 부하를 줄이고 연결 관리 한다.
    • pool에 미리 connection이 생성되어 있기 때문에 connection을 생성하는 데 드는 요정 마다 연결 시간이 소비되지 않는다.
    • 커넥션을 계속해서 재사용하기 때문에 생성되는 커넥션 수를 제한적으로 설정함

     

    동시 접속자가 많을 경우

    • 위에 커넥션 풀 설명에 따르면, 동시 접속 할 경우 pool에서 미리 생성 된 connection을 제공하고 없을 경우는 사용자는 connection이 반환될 때까지 번호순대로 대기상태로 기다린다.
    • 여기서 WAS에서 커넥션 풀을 크게 설정하면 메모리 소모가 큰 대신 많은 사용자가 대기시간이 줄어들고, 반대로 커넥션 풀을 적게 설정하면 그 만큼 대기시간이 길어진다.

    Commons DBCP 구조

    속성이름 설명
    initialSize BasicDataSource 클래스 생성 후 최초로 getConnection()메서드를 호출할 때 커넥션 풀에 채워 넣을 커넥션 갯수
    maxActive 동시에 사용할 수 있는 최대 커넥션 갯수(개본값: 8)
    mavIdle 커넥션 풀에 반납할 때 최대로 유지될 수 잇는 커넥션 갯수(기본값 : 8)
    minIdle 최소한으로 유지할 커넥션 갯수(기본값 : 0)

    • 8개의 커넥션을 최대로 활용할 수 있는 상태
    • 4개는 사용 중이고 4개는 대기 중인 상태

    커넥션 개수와 관련된 속성은 다음과 같은 조건을 논리적 오류가 없게 설정하는걸 추천한다.

    • maxActive >= initialSize
    • maxIdle >= minIdle
    • maxActive = maxIdle

    커넥션 풀 종류

    • Commons DBCP
    • Tomcat-JDBC
    • BoneCP
    • HikariCP

     

    DriverManagerDataSource 사용시오류 발생 제어방법

    1. timer, wait 를 사용해서 인의적으로 DB Connection 을 늦추면 된다.

    2. 큐를 이용하여 작업을 제어한다. 

     

    -> 웹 서비스 환경에 맞지 않음..

     

     

    그냥 커넥션 풀 사용하자...

     

     

     

     

     

     

    참고

    https://linked2ev.github.io/spring/2019/08/14/Spring-3-%EC%BB%A4%EB%84%A5%EC%85%98-%ED%92%80%EC%9D%B4%EB%9E%80/

    반응형
Designed by Tistory.