ABOUT ME

와주셔서 감사합니다. 좋은 글을 많이 쓰겠습니다.

Today
Yesterday
Total
  • [HikariCP] idleTimeout 알아보기
    Java 2024. 7. 18. 06:08
    반응형

    - 목차

     

    idleTimeout 이란 ?

    HikariPool DataSource 는 maximumPoolSize 를 통해서 최대 Connection 의 갯수를 지정할 수 있습니다.

    만약에 maximumPoolSize 를 6개로 설정한다면 아래의 이미지처럼 하나의 Connection Pool 은 최대 6개의 Connection 을 보유합니다. 

     

    Connection Pool 이 데이터베이스와 맺는 이 Connection 은 사실상 TCP Network Connection 이며, 아래의 출력 결과와 같이 TCP Socket 이 생성되게 됩니다.

    ( lsof 명령어나 MySQL 의 show processlist; 명령어 등을 통해서 현재 생성된 TCP Socket 을 확인할 수 있습니다.  )

    lsof -i
    COMMAND PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
    java    736 root    9u  IPv6  80835      0t0  TCP fbbbd46ffc0b:44556->mysql.mysql:mysql (ESTABLISHED)
    java    736 root   12u  IPv6  80838      0t0  TCP fbbbd46ffc0b:44572->mysql.mysql:mysql (ESTABLISHED)
    java    736 root   13u  IPv6  81657      0t0  TCP fbbbd46ffc0b:44580->mysql.mysql:mysql (ESTABLISHED)
    java    736 root   14u  IPv6  87099      0t0  TCP fbbbd46ffc0b:44588->mysql.mysql:mysql (ESTABLISHED)
    java    736 root   16u  IPv6  85303      0t0  TCP fbbbd46ffc0b:44596->mysql.mysql:mysql (ESTABLISHED)
    show processlist;
    +----+-----------------+------------------+-------+---------+------+------------------------+------------------+
    | Id | User            | Host             | db    | Command | Time | State                  | Info             |
    +----+-----------------+------------------+-------+---------+------+------------------------+------------------+
    |  5 | event_scheduler | localhost        | NULL  | Daemon  | 8533 | Waiting on empty queue | NULL             |
    | 68 | root            | localhost        | NULL  | Query   |    0 | init                   | show processlist |
    | 69 | root            | 172.18.0.3:48106 | mysql | Sleep   |    1 |                        | NULL             |
    | 70 | root            | 172.18.0.3:48112 | mysql | Sleep   |    1 |                        | NULL             |
    | 71 | root            | 172.18.0.3:48120 | mysql | Sleep   |    1 |                        | NULL             |
    | 72 | root            | 172.18.0.3:48130 | mysql | Sleep   |    1 |                        | NULL             |
    | 73 | root            | 172.18.0.3:48138 | mysql | Sleep   |    1 |                        | NULL             |
    +----+-----------------+------------------+-------+---------+------+------------------------+------------------+
    7 rows in set (0.00 sec)

     

    이때에 어떠한 TCP Packet 도 전송되지 않는 Idle 상태의 Connection 은 사실상 시스템에 있어서 자원낭비에 해당합니다.

    OS 커널 입장에서도 Socket 을 관리해야하고, 이러한 소켓을 위한 별도의 메모리 공간도 필요하며,

    하나의 Process 가 가질 수 있는 FD 갯수도 제한적이기 때문에 Idle Connection 은 리소스가 낭비되는 상황이 됩니다.

    따라서 HikariPool 은 이러한 Idle Connection 에 Idle Timeout 을 적용하여 특정 시간동안 어떠한 TCP Packet 이 전송되지 않는 Connection 은 제거하게 됩니다.

     

    IdleTimeout 을 직접 적용해보기.

    아래의 코드는 IdleTimeout 은 10초로 설정하고, 2개의 Min Connection 과 5개의 Max Connection 을 가지는 Pool 을 생성합니다. 

    import com.zaxxer.hikari.HikariConfig;
    import com.zaxxer.hikari.HikariDataSource;
    import java.sql.Connection;
    import java.sql.SQLException;
    
    public class HikariPoolMonitor {
        public static void main(String[] args) throws InterruptedException {
            // HikariCP 설정
            HikariConfig config = new HikariConfig();
            config.setJdbcUrl("jdbc:mysql://mysql:3306/mysql");
            config.setUsername("root");
            config.setPassword("1234");
            config.setMinimumIdle(2);
            config.setMaximumPoolSize(5);
            config.setIdleTimeout(10000);
    
            // Hikari DataSource 생성
            HikariDataSource dataSource = new HikariDataSource(config);
            try {
                Connection c1 = dataSource.getConnection();
                Connection c2 = dataSource.getConnection();
                Connection c3 = dataSource.getConnection();
                Connection c4 = dataSource.getConnection();
                Connection c5 = dataSource.getConnection();
                c1.close();
                c2.close();
                c3.close();
                c4.close();
                c5.close();
            } catch (SQLException e) {}
    
            // 5초 단위로 HikariCP 풀 상태를 출력하는 루프
            while (true) {
                printHikariPoolStatus(dataSource);
                Thread.sleep(5000); // 5초 대기
            }
    
            // 데이터소스 종료
            // dataSource.close();
        }
    
        // HikariCP 풀 상태 출력 함수
        private static void printHikariPoolStatus(HikariDataSource ds) {
            System.out.println("========== HikariCP Pool 상태 ==========");
            System.out.println("총 커넥션 수: " + ds.getHikariPoolMXBean().getTotalConnections());
            System.out.println("활성 커넥션 수: " + ds.getHikariPoolMXBean().getActiveConnections());
            System.out.println("유휴 커넥션 수: " + ds.getHikariPoolMXBean().getIdleConnections());
            System.out.println("대기 중인 스레드 수: " + ds.getHikariPoolMXBean().getThreadsAwaitingConnection());
            System.out.println("====================================\n");
        }
    }

     

    위 코드가 실행되면 아래와 같은 출력을 뱉습니다.

    idleTimeout 인 10초가 지나게 되면 최대 5개에서 2개의 Connection 으로 Connection Pool 의 사이즈가 줄어듦을 확인할 수 있습니다.

    [main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting...
    [main] INFO com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@45c7e403
    [main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Start completed.
    ========== HikariCP Pool 상태 ==========
    총 커넥션 수: 5
    활성 커넥션 수: 0
    유휴 커넥션 수: 5
    대기 중인 스레드 수: 0
    ====================================
    
    
    ========== HikariCP Pool 상태 ==========
    총 커넥션 수: 2
    활성 커넥션 수: 0
    유휴 커넥션 수: 2
    대기 중인 스레드 수: 0
    ====================================

     

     

    TCP 관점에서 살펴보기.

    tcpdump 명령어를 통해서 Idle Connection 의 TCP Packet 전달 과정을 확인합니다.

    Idle Connection 은 정말 어떠한 TCP Packet 도 보내지 않습니다.

    그래서 TCP Packet 을 포착하지 못하였구요.

    대신 아래의 TCP Packet 들은 Connection 의 종료를 알리는 4 way handshake 단계의 Packet 들입니다.

    Flag 가 F 또는 R 인 Packet 들이 보이며, 이것은 Finish 와 Reset 을 의미합니다.

    그리고 이러한 Flag 의 Packet 은 Connection 의 종료를 의미합니다.

     

    13:53:16.937562 IP mysql.3306 > zealous_pascal.mysql.45256: Flags [F.], seq 4112, ack 2044, win 524, options [nop,nop,TS val 131635901 ecr 2691528606], length 0
    	0x0000:  4500 0034 3847 4000 4006 aa53 ac12 0002  E..48G@.@..S....
    	0x0010:  ac12 0003 0cea b0c8 4610 34de e072 c36c  ........F.4..r.l
    	0x0020:  8011 020c 5850 0000 0101 080a 07d8 9abd  ....XP..........
    	0x0030:  a06d 779e                                .mw.
    13:53:16.938476 IP mysql.3306 > zealous_pascal.mysql.45256: Flags [R], seq 1175467231, win 0, length 0
    	0x0000:  4500 0028 0000 4000 4006 e2a6 ac12 0002  E..(..@.@.......
    	0x0010:  ac12 0003 0cea b0c8 4610 34df 0000 0000  ........F.4.....
    	0x0020:  5004 0000 1f15 0000                      P.......
    13:53:16.944338 IP mysql.3306 > zealous_pascal.mysql.45254: Flags [F.], seq 4112, ack 2124, win 524, options [nop,nop,TS val 131635908 ecr 2691528613], length 0
    	0x0000:  4500 0034 1082 4000 4006 d218 ac12 0002  E..4..@.@.......
    	0x0010:  ac12 0003 0cea b0c6 7253 385e 8f02 ebbb  ........rS8^....
    	0x0020:  8011 020c 5850 0000 0101 080a 07d8 9ac4  ....XP..........
    	0x0030:  a06d 77a5                                .mw.
    13:53:16.944406 IP mysql.3306 > zealous_pascal.mysql.45254: Flags [.], ack 2125, win 524, options [nop,nop,TS val 131635908 ecr 2691528613], length 0
    	0x0000:  4500 0034 1083 4000 4006 d217 ac12 0002  E..4..@.@.......
    	0x0010:  ac12 0003 0cea b0c6 7253 385f 8f02 ebbc  ........rS8_....
    	0x0020:  8010 020c 5850 0000 0101 080a 07d8 9ac4  ....XP..........
    	0x0030:  a06d 77a5                                .mw.
    13:53:16.947459 IP mysql.3306 > zealous_pascal.mysql.45238: Flags [F.], seq 4112, ack 2124, win 524, options [nop,nop,TS val 131635911 ecr 2691528616], length 0
    	0x0000:  4500 0034 c192 4000 4006 2108 ac12 0002  E..4..@.@.!.....
    	0x0010:  ac12 0003 0cea b0b6 596a b8c0 246c 6e76  ........Yj..$lnv
    	0x0020:  8011 020c 5850 0000 0101 080a 07d8 9ac7  ....XP..........
    	0x0030:  a06d 77a8                                .mw.
    13:53:16.947494 IP mysql.3306 > zealous_pascal.mysql.45238: Flags [.], ack 2125, win 524, options [nop,nop,TS val 131635911 ecr 2691528616], length 0
    	0x0000:  4500 0034 c193 4000 4006 2107 ac12 0002  E..4..@.@.!.....
    	0x0010:  ac12 0003 0cea b0b6 596a b8c1 246c 6e77  ........Yj..$lnw
    	0x0020:  8010 020c 5850 0000 0101 080a 07d8 9ac7  ....XP..........
    	0x0030:  a06d 77a8                                .mw.

     

     

    관련 명령어들.

    docker network create mysql 
    docker run -d --platform linux/amd64 --network mysql --name mysql --hostname mysql -e MYSQL_ROOT_PASSWORD=1234 mysql:8.0.23
    docker run -it --network mysql --rm -v /tmp/HikariPoolMonitor.java:/tmp/HikariPoolMonitor.java openjdk:11 /bin/bash
    
    cd /tmp/
    wget https://repo1.maven.org/maven2/com/zaxxer/HikariCP/5.1.0/HikariCP-5.1.0.jar
    wget https://repo1.maven.org/maven2/org/slf4j/slf4j-api/1.7.36/slf4j-api-1.7.36.jar
    wget https://repo1.maven.org/maven2/org/slf4j/slf4j-simple/1.7.36/slf4j-simple-1.7.36.jar
    wget https://repo1.maven.org/maven2/com/mysql/mysql-connector-j/8.0.33/mysql-connector-j-8.0.33.jar -P /tmp
    
    javac -cp HikariCP-5.1.0.jar:slf4j-api-1.7.36.jar:slf4j-simple-1.7.36.jar:mysql-connector-j-8.0.33.jar HikariPoolMonitor.java
    java -cp .:HikariCP-5.1.0.jar:slf4j-api-1.7.36.jar:slf4j-simple-1.7.36.jar:mysql-connector-j-8.0.33.jar HikariPoolMonitor

     

    반응형
Designed by Tistory.