
김태희님의 게시물을 보고 적용해보면서 작성하는 글.
글에 보면 이런 내용이 나온다.
I/O-bound인 경우 Thread per Request model의 경우 하드웨어 스레드보다 많은 Thread를 생성하면 I/O작업으로 인해 CPU에 Thread가 할당 되지 않는 경우를 방지하여 성능을 향상시킬 수 있다. 하지만 이 경우 또한 너무 많은 스레드 개수는 Context Switching으로 인한 Overhead만 증가시킬 수 있다.
여기에서 말하는 CPU bound 작업이란, CPU 연산을 많이 차지하는 작업 즉, 대량의 사진을 고해상도로 변환하는 등의 작업을 뜻한다. 반면, IO bound 작업이란 파일 입출력, 네트워크 통신, 데이터베이스 작업 등을 뜻한다.
대부분의 Spring Server에서 이루어지는 IO Bound 작업일 것으로 추정이 된다.
검색 결과 Thread Pool과 DB Pool을 각각 코어 수의 두 배 정도 주는게 좋다는 글이 많았지만, Docker 서버를 아래와 같이 2코어 4gb 램을 가지고 있도록 제한을 두었기 때문에 Thread pool과 DB Pool을 4씩 주는건 너무 적게 느껴졌다.
deploy:
resources:
# https://docs.docker.com/reference/compose-file/deploy/#resources
limits: # 제한
cpus: '2'
memory: 4g
reservations: # 메인 OS에서 미리 예약
cpus: '2'
memory: 4g
그래서 내가 임의로 좋을 것 같다고 생각한 설정을 임의로 넣었다.
내가 임의로 설정한 파일은 다음과 같고 테스트 조건은 500명의 유저가 동시에 게시물을 작성하는 환경을 1분간 테스트를 진행한 것이다.
hikari:
maximum-pool-size: 60
minimum-idle: 10
connection-timeout: 30000
idle-timeout: 600000 # 연결의 최대 유지 시간 ms 단
max-lifetime: 1800000 # 연결의 최대 수명 ms 단위
server:
tomcat:
threads:
max: 1000 # 최대 동시 요청수, 기본 200
min-spare: 200 # 최소한 유지하는 스레드 수, 기본 10
max-connections: 4000
# 대기 중인 연결 큐의 최대 크기

TPS는 50, 평균 응답 속도는 8.47이 나온 모습을 볼 수 있다.

spring:
datasource:
hikari:
maximum-pool-size: 20
minimum-idle: 5
connection-timeout: 30000
idle-timeout: 600000 # 연결의 최대 유지 시간 ms 단
max-lifetime: 1800000 # 연결의 최대 수명 ms 단위
server:
tomcat:
threads:
max: 40 # 최대 동시 요청수, 기본 200
min-spare: 10 # 최소한 유지하는 스레드 수, 기본 10
max-connections: 500 # 기본 8192
# 대기 중인 연결 큐의 최대 크기
# 대기열이 꽉 차면 추가 요청은 거절됨 (기본값 100)
accept-count: 200 # 대기 중인 연결 큐의 최대 크기, 기본 100
connection-timeout: 180000 # 3분, 클라이언트 연결 대기 시간

TPS는 109, 평균 응답 속도는 3.43이 나온 모습을 볼 수 있다.

무작정 Thread Pool을 높이고 DB Pool을 높이는게 좋은게 아니다. 왜냐면 Context Switch가 지속적으로 발생함으로써 오히려 처리량이 감소하기 때문이다.