HikariDataSource초기화
- 먼저 아래와 같은 옵션으로 DataSource를 빈으로 등록했다.
- 생성자에 HikariConfig를 인자로 넘긴다.
- maximumPoolSize: 3
- maxLifetime: 10분
- HikariConfig를 인자로 넘겼기 때문에 fastPathPool도 같이 초기화 하는것을 확인할 수 있다.
- 이제 HikariPool을 초기화하는 과정을 알아보자.
HikriPool 초기화
- 2편에서와 마찬가지고 아래의 과정으로 나눠보았다.
- HikariPool을 초기화하는 과정을 아래와 같은 단계로 나눠보았다.
- DataSource 생성
- connectionBag 생성
- houseKeepingExecutorService 생성
- 커넥션 연결 확인
- 커넥션 유지
- 커넥션 추가
DataSource 생성
- HikariPool의 상위타입인 PoolBase에서 DataSource를 생성한다.
- initializeDataSource() 메소드에서 HikariConfig에 있는 옵션들을 바탕으로 331번째 줄에서 DataSource를 생성한다.
connectionBag 생성
- 실질적으로 Connection Pool역할을 하는 변수다.
- ConcurrentBag 타입의 변수이며, 아래와 같이 내부적으로 threadList, sharedList, handOffQueue를 가지고 있다.
- 아래 코드를 보면 threadList, sharedList, handOffQueue를 초기화하는 것을 확인할 수 있다.
houseKeepingExecutorService 생성
- houseKeepingExecutorService는 idle Connection의 수를 주기적으로 체크하는 역할을 한다.
- houseKeeper라는 이름으로 threadFactory를 만든다.
- daemon thread를 true 설정했는데, 여기서 데몬 스레드는 주 스레드의 작업을 돕는 보조적 역할을 담당하는 쓰레드로이며 주 스레드가 종료되면 데몬 스레드도 같이 종료된다.
- 다음으로 ScheduledThreadPoolExecutor 타입으로 houseKeepingExecutorService를 생성하면서 인자로 좀전에 만든 threadFactory를 넘긴다.
- ScheduledThreadPoolExecutor는 어떤 작업을 주기적으로 실행하거나 일정 시간 지연 후에 수행할때 사용된다.
- houseKeeper가 주기적으로 idle connection 수를 체크하기 때문에 ScheduledThreadPoolExecutor가 적합하다.
- scheduleWithFixedDelay 메소드를 사용해서 작업이 완료되고 일정 시간 뒤에 다시 작업을 실행한다.
addConnectionExecutor 생성
- addConnectionExecutor 는 이름 그대로 커넥션을 생성한다.
- 커넥션을 추가하는 시점에 addConnectionExecutor가 createPoolEntry() 메소드를 호출해서 addConnectionExecutor의 스레드풀의 스레드로 커넥션을 생성한다.
- 이 부분은 아래에 커넥션을 추가하는 과정 설명에서 좀 더 자세하게 설명한다.
커넥션 연결 확인
- checkFailFast() 메소드를 통해, 연결 확인용으로 1개의 커넥션만 맺어보고, 커넥션 풀에 넣어둔다.
- createPoolEntry() 메소드를 통해서 1개의 poolEntry를 연결 확인용으로 만들어 본다.
- 그 뒤, connectionBag에 만들어진 poolEntry를 넣는다.
- connectionBag의 sharedList를 보면 NOT_IN_USE 상태의 커넥션이 하나 담겨있는 것을 확인 할 수 있다.
커넥션 유지
- 현재 maximumPoolSize를 3으로 설정해서 위에서 커넥션 연결 확인을 위해 1개의 poolEntry를 connectionBag에 넣었기 때문에 connectionToAdd인 것을 확인할 수 있다.
- 실제로 fillPool() 메소드에 브레이크 포인트를 걸어보면 일정 주기로 fillPool() 메소드가 호출되며, submit의 반환값이 Future인 것을 보면 별로의 스레드의 비동기적으로 커넥션을 채운다는 것을 확인할 수 있다.
- 그렇다는 것은 커넥션풀이 10이더라도 서버가 구동된다음 커넥션풀이 10개가 될때까지 기다리는 것이 아니라 checkFailFast() 1개의 커넥션만 확인한 다음 나머지 9개는 서버가 뜨면서 비동기적으로 채워진다는 것을 알 수 있었다.
- houseKeepingExecutorService 가 HouseKeeper를 동작시키는 주체이며, houseKeepingExecutorService를 생성할 때 만들어둔 별도의 스레드풀에서 비동기적으로 실행된다.
- 이 부분이 이해가 되지 않는다면, java 1.5 버전에 등장한 Future와 ExecutorService를 공부해보면 좋을 것 같다. 참고로 자바 1.8부터 CompletableFuture를 더 많이 사용한다.
커넥션 추가
- 1편에서 설명했듯이, 커넥션이 필요한 시점에 ConcurrentBag.borrow() 메소드에서 커넥션을 반환받는다.
- borrow() 메소드에서 현재 sharedList에 사용가능한 커넥션이 없다면, 커넥션을 추가한다.
- addBagItem() 메소드에서 submit() 메소드를 호출해서 비동기적으로 커넥션을 추가하면, addConnectionExecutor가 createPoolEntry()메소드를 통해서 PoolEntry에 커넥션을 담아서 반환해준다.
이번편에서는 HikariPool을 초기화하는 과정을 알아보았다. 다음편에서 커넥션을 점유하고 반납하는 과정에 대해서 알아보자
References
'Jdbc' 카테고리의 다른 글
HikariCP 코드 분석하기 4편 (Connection 점유, Connection 반납) (0) | 2023.01.22 |
---|---|
HikariCP 코드 분석하기 2편 (HikariCP 커넥션 풀 초기화 과정) (0) | 2023.01.21 |
HikariCP 코드 분석하기 1편 (HikariCP란?) (0) | 2023.01.21 |
JOOQ (Java Object Oriented Querying) (0) | 2022.03.24 |
Spring JDBC (0) | 2022.02.27 |