URL-CHOP / Chop-Api

🔪App Server for URL shortener
https://www.nexters.me/
6 stars 1 forks source link

[커넥션] 커넥션 풀 정리 #66

Closed choitaehoon closed 5 years ago

choitaehoon commented 5 years ago

커넥션 풀

  • 데이터 베이스와 애플리케이션을 연결하는 커넥션 풀 라이브러리는 웹 애플리케이션에서 필수적인 요소 이다. 커넥션 풀 라이브러리를 잘 사용하게 된다면 데이터베이스와 애플리케이션 의 일부분 에서 발생하는 문제가 전파 되지 않는다. 반대로 값을 제대로 주지 못하게 되면 데이터베이스와 애플리케이션의 사이가 병목 지점이 될 수 있다. 이렇게 중요한 커넥션 풀 라이브러리를 적절하게 사용하려면 라이브러리의 내부 구조와 원리,속성값을 제대로 이해 해야 할 것 이다.

  • 커넥션 풀 사용 하는 이유는?

    277562435975413505

  • 클라이언트에서 다수의 요청이 들어오면 데이터베이스에 부하가 걸린다.그리고 요청때 마다 커넥션을 만들게 된다면 상당히 느린 작업으로 실행할 것 이다. 커넥션은 비용이 크기 때문에 계속 만들게 될경우 커넥션에서 병목지점이 될 것이다. 커넥션 풀은 풀 안에 커넥션을 미리 만들어 두고 요청이 올때 풀 안에 미리 있던 커넥션을 주고 사용자가 다 쓰고 반환하게 되면 다시 풀 안으로 집어 넣어 재활용 하는 방식으로 사용함으로써 애플리케이션 수행 속도도 빠를 뿐 아니라 동시 요청이 들어와도 감당 할 수 있다.

  • 커넥션풀 저장 구조

    helloworld-201508-commonsdbcp-------1

  • 커넥션 생성은 commons DBCP에서 이루어진다. commons DBCP는 Poolable Connection 타입의 커넥션을 생성하고 생성한 커넥션에 connectionEventListener 이벤트를 등록한다. 이 이벤트 에는 애플리케이션에서 반환된 커넥션을 반환 하기 위한 콜백 메소드가 있다. commons-pool은 내부적으로 현재시간 을 담고있는 타임스탬프와 추가된 커넥션의 레퍼런스를 한쌍으로 하는 ObjectTimestampPair이라는 자료구조를 생성한다. 이것은 LIFO 형태로 담고 있다

  • 커넥션 개수 관련 속성

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


    커넥션을 얻기 전 대기 시간

  • BasicDataSource 클래스의 maxWait 속성은 커넥션 풀안에 커넥션이 없을때 커넥션 반납하는 시간이며 기본값은 무한대 이다. 적절한 maxWait 값을 설정하려면 TPS와 tomcat의 처리 가능한 스레드의 이해가 필요하다.

  • TPS

    image

  • 그림을 보면 사용자 요청 A는 요청 하나에 쿼리 10개 실행한다고 가정하면 각 쿼리의 평균 시간은 50밀리초 라고 하면 50*10 하면 500밀리초 이다.

  • image

  • 위 그림을 분석하면 요청 하나당 500밀리초 이고 커넥션 풀에 이용 가능한 커넥션 개수가 5개 이면 동시에 5개의 커넥션이 동시에 5개의 요청을 500밀리초 동안 처리한다 그렇다면 1초에 10개의 요청을 처리 할 수 있고 이 성능 지수를 10TPS라고 한다.

  • TPS와 커넥션 개수와의 관계

    image

  • 이 그림을 보면 요청이 더 많이 들어와도 커넥션 개수가 5개 이므로 10TPS 이상의 성능을 낼 수 없다 6번 부터 10번까지의 요청은 대기 상태가 되어 여분의 커넥션이 생길때 까지 maxWait값만큼 기다린다.이때 기다리고 있다는 에러가 나온다.
  • "TP-Processor104" daemon prio=10 tid=0x00007f76e8093800 nid=0x2d80 in Object.wait() [0x00007f76f905a000]
    java.lang.Thread.State: TIMED_WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)

    • waiting on <0x00000000f886fee0> (a org.apache.commons.pool.impl.GenericObjectPool$Latch) at org.apache.commons.pool.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:1112)
    • locked <0x00000000f886fee0> (a org.apache.commons.pool.impl.GenericObjectPool$Latch) at org.apache.commons.dbcp.PoolingDataSource.getConnection(PoolingDataSource.java:106) at org.apache.commons.dbcp.BasicDataSource.getConnection(BasicDataSource.java:1044) 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:80)


    적당한 maxWait의 값을 주어야 하는데..

  • 이 부분을 깊게 이해하기 위해서는 DBCP이외에 tomcat의 동작방식도 잘 알아야 한다. tomcat 방식에 대해 깊게 보도록 해보자!

  • tomcat 동작 방식

    image

  • tomcat은 스레드 기반으로 동작해 사용자의 요청을 처리한다. DBCP 처럼 tomcat도 내부에 스레드 풀을 가지고 있다. 그림처럼 사용자 요청이 들어올때 마다 스레드 풀에서 하나씩 스레드를 꺼내 요청을 처리한다. 그림을 보면 동시에 6개의 요청이 왔을때 6번 스레드는 여분의 커넥션이 없으므로 maxWait값 만큼 기다리고 있다. 여기서 중요한 것은 기다리는 주체가 tomcat의 스레드 라는 점이다.

  • maxWait의 상태를 기다리고 커넥션을 획득해 사용자의 요청을 열심히 처리하고 응답을 보내도 이미 사용자가 없는 경우 이다. 보통 사람들은 클릭 후 2~3초 동안 반응이 없으면 새로고침 하거나 나가게 된다. 기다리는 사람도 없는데 커넥션을 받고 요청의 대한 응답을 처리하고 보내도 받을 사람이 없어 자원을 낭비하게 된다는 점이다.. maxWait를 너무 작게 잡으면 커넥션이 없을때 마다 에러를 보내고 사용자는 많은 에러를 접하게 된다. 그래서 커넥션 검사와 정리가 필요하다.
  • Chop 프로젝트 문제

  • 지금 현재 애플리케이션과 디비와의 연결 해주는 커넥션이 일정 시간(8시간 이상) 사용하지 않으면 저절로 끊기게 된다. 그래서 sql 에러가 나오고 페이지에도 요청을 할 수 없다는 에러가 나온다.
  • org.springframework.dao.DataAccessResourceFailureException: Unable to acquire JDBC Connection; nested exception is org.hibernate.exception.JDBCConnectionException: Unable to acquire JDBC Connection
    Caused by: org.hibernate.exception.JDBCConnectionException: Unable to acquire JDBC Connection

    Chop 프로젝트 문제 해결 전략

  • 그러면 주기적으로 tomcat에서 요청을 보내 그 커넥션이 살아 있는지 확인하고 살아 있다면 남기고 죽었다면 버린다.
  • 커넥션 설정

    속성 이름 설명
    validationQuery connection 유효성 검사시에 사용할 쿼리문 -> db리소스를 최대한 작게 잡는게 좋다 ex)select 1;
    testWhileIdle 놀고 있는 connection의 제거 여부를 검사한다. 기본값은 false 라서 true로 설정해야 한다
    timeBetweenEvictionRunsMillis 놀고 있는 connection을 pool에서 제거하는 시간 기준이다.
    aj1155 commented 5 years ago

    굿굿~