20.5 웹 애플리케이션 서버 튜닝
웹 애플리케이션 서버 튜닝에서는 실행 스레드, 기본 I/O, JDBC 커넥션 풀링, EJB, 클러스터링 5가지 항목을 설명한다.
데이터베이스 커넥션 풀링
새롭게 구축되는 시스템에서 성능을 최대로 발휘하기 위해서 데이터베이스 커넥션 풀과 스레드 풀을 튜닝하는 것은 중요한 부분이다.
시스템 리소스 관점에서 보면 데이터베이스 커넥션을 열거나 닫는 것은 매우 비용이 많이 드는 작업이다. 최근에는 많지는 않지만, 애플리케이션 개발 시 데이터베이스 조회나 트랜잭션에 대해 매번 새로운 커넥션을 생성하고 바로 닫는 경우도 있다. 이러한 방식은 트랜잭션 처리에 큰 오버헤드가 발생하여 성능 감소의 원인이 된다. JBoss EAP6에서 데이터베이스 커넥션 풀 기능을 제공하고 있으니, 이를 이용하는 방법이 성능이나 안정적인 서비스를 위해 권장한다.
스레드 튜닝
스레드 풀은 애플리케이션의 성능을 튜닝 하는 데 있어서 두 번째로 중요한 부분이다. JBoss EAP 6는 견고한 스레드 풀 기능을 갖추고 있지만 이를 운영 환경에 적합하게 설정하기 위해서는 각각의 스레드 풀의 용도와 애플리케이션 성능에 미치는 영향을 파악해야 한다.
애플리케이션 종류에 따라 사용하는 스레드 풀이 다르며 어떤 부분이 병목이 있는지도 다르다.
애플리케이션 종류나 사용 방식에 따라 매우 달라서 주의가 필요하다.
스레드 수가 많다고 무조건 성능이 향상되지는 않는다. 스레드는 CPU 성능과 관련이 깊어 스레드 수가 성능에 미치는 영향은 다음과 같다.
- 스레드 수가 너무 많은 경우 스레드 관리를 위한 오버헤드가 증가해서 반대로 성능이 감소한다. CPU 사용량이 피크에 도달한 상황에서는 스레드 수를 줄이면 성능이 향상될 수 있다.
- 스레드 수가 너무 적은 경우 CPU 사용률이 감소하고 지연 시간이 발생할 수 있다. 이 경우 스레드 수를 늘려 성능을 향상할 수 있다.
최적의 스레드 수는 시스템 구성(특히 CPU 수, CPU 성능)에 따라 각각 다르다. 부하 테스트 등으로 스레드 수의 최적 값을 찾아야 한다.
스레드 튜닝 시 유의점
- CPU의 사용량이 100%가 아닐 때 클라이언트의 요청이 자주 블록되거나 거절되는 경우에만 실행 스레드 수를 튜닝하라.
- 스레드 수를 튜닝할 때 처리량이 떨어지거나 CPU 사용량이 떨어지거나 일정하게 유지되는 경우에는 튜닝을 중지하라.
- 애플리케이션 컴포넌트를 파티셔닝하거나 지정된 수만큼의 자원을 컴포넌트에 할당하기 위해서는 사용자 정의(user-defined)된 실행 큐를 설정한다.
- 커스텀 실행 큐를 사용하면 잠재적인 크로스서버 데드락(cross-server deadlock)을 방지할 수 있다.
- 메세지 드리븐 빈(Message-driven Bean)에 지정된 리소스를 할당하기 위해서는 배치된 각각의 메세지 드리븐 EJB마다 개별적인 실행 큐를 사용하라.
- JBoss EAP에서 오랫동안 실행되는 요청이나 데드락을 해결하려면, 원인을 찾기 위해서 적당한 간격의 일련의 스레드 덤프(thread dump)를 받아 분석하라. 예를 들어 3~5초 간격 5회 스레드 덤프를 받아 분석한다.
웹 서브시스템
JBoss EAP 6만 사용하여 웹 시스템을 구축할 수 있지만, 일반적으로는 JBoss EAP 6의 앞단에 Apache 웹 서버를 배치해, 정적 콘텐츠(HTML, CSS, 이미지 등의 정적 파일)의 전달이나 로드 밸선싱을 수행한다. 아파치 웹서버를 이용하여 로드 밸런싱을 구축하는 경우, mod_jk 또는 mod_cluster 등의 모듈을 사용하여 소프트웨어 로드 밸런스형태로 사용한다.

그림 7. 웹 애플리케이션 서버 시스템의 일반 구성
위 그림은 웹 애플리케이션 서버 시스템의 일반적인 구성이다. 서버 시스템은 외부에서 HTTP 요청을 받고 HTTP 응답을 반환하는 웹 서버, 웹 서버에서 전달된 요청에 대한 업무를 처리하는 애플리케이션 서버, 업무 처리 결과 데이터를 업데이트하는 데이터베이스 서버로 구성된다.
동시 접속수
웹 서브시스템은 HTTP/1. 1 커넥터(기본 포트 번호 8080) 및 AJP/1. 3 커넥터(기본 포트 번호 8009)를 사용해 앞 단의 웹 서버와 통신한다. 여기에서는 주로 HTTP 혹은 AJP 요청을 처리하는 JBoss EAP 6의 워커 스레드 수를 ‘동시 접속 수’로 튜닝하는 방법을 소개한다.
웹 서브시스템은 클라이언트 요청에 대해서 스레드를 할당해 처리한다. 웹 시스템은 이러한 요청을 처리하기 위하여 워커 스레드를 사용하게 되며, 워커 스레드의 생성과 소멸을 효율적으로 처리하기 위해 내부적으로 쓰레드 풀링을 사용한다.
웹 서브시스템은 클라이언트의 요청을 수신하면 풀에서 대기 상태의 스레드를 할당하고 요청이 완료(클라이언트에의 응답 완료 시)되면 스레드 풀에 반환한다. JBoss EAP 6의 앞단에 Apache 웹 서버를 설치한 경우 요청 처리 완료 후에도 Apache와 JBoss EAP 6간의 연결은 일정 기간 연결된 상태로 남아있어, 스레드 풀로 반환은 연결이 완전히 끊어진 이후가 된다.

그림 8. 웹 서버와 애플리케이션 서버에서 사용하는 풀
요청이 들어왔을 때 스레드 풀의 최대값에 도달하지 않는 경우 새로운 스레드를 생성하여 할당한다. 스레드 풀이 최대값에 도달한 경우에는 클라이언트와 연결되지 않는다.
클라이언트의 동시 접속 수는 이 스레드 풀의 최대값에 의해 결정된다. 스레드 풀은 커넥터마다 생성하여 관리한다. 여러 개의 웹 애플리케이션이 배포된 서버에서는 같은 커넥터를 사용해 애플리케이션간에 스레드 풀을 공유한다.
스레드 풀의 최대값은 커넥터의 max-connections 속성에 설정한다. 동시접속 수는 애플리케이션 서버보다 앞에 설치하는 웹 서버의 최대 동시 접속 수나 웹 서버의 대수를 고려해 값을 계산한다. max-connections로 설정한 값이 웹 서버의 동시접속 수보다 적은 경우, 웹 서브시스템의 워커 스 레드가 부족하여, 접속 에러가 발생할 수 있어 주의해야 한다.
- AJP/1. 3 커넥터
| 위치 | 속성 | 설명 |
|---|---|---|
| /subsystem=web/connector=ajp | max-connections | 워커 스레드 풀의 최대값. 기본값은 JVM이 사용할 수 있는 CPU 코어수 × 512 이다. |
- HTTP/1. 1 커넥터
| 위치 | 속성 | 설명 |
|---|---|---|
| /subsystem=web/connector=http | max-connections | 워커 스레드 풀의 최대값. 기본값은 JVM이 사용할 수 있는 CPU 코어수 × 512 이다. |
동시 접속 수 튜닝
다음은 CLI를 이용하여 AJP/1. 3 커넥터의 max-connections의 값을 2000으로 설정하는 예이다.
-
웹 서브시스템의 AJP 커넥터의 max-connections속성을 2000으로 설정한다.
[standalone@localhost:9999 /] cd /subsystem=web/connector=ajp[standalone@localhost:9999 connector=ajp] :write-attribute(name=max-connections, value=2000){"outcome" => "success","response-headers" => {"operation-requires-reload" => true,"process-state" => "reload-required"}} -
reload 한다.
[standalone@localhost:9999 /] /:reload{"outcome" => "success"} -
웹 서브시스템의 AJP 커넥터의 max-connections속성을 확인한다.
[standalone@localhost:9999 /] cd /subsystem=web/connector=ajp[standalone@localhost:9999 connector=ajp] :read-attribute(name=max-connections){"outcome" => "success","result" => 2000,"response-headers" => {"process-state" => "reload-required"}}-
max-connections 산정 방법
max-connections 의 값을 결정하는 계산 식은 다음과 같다.
-
💡 TIP
max-connections = (Apache 의 MaxClients) X (웹 서버 수) X α
※ MaxClients 는 Apache 웹서버의 MPM 에서 설정한 최대동시접속자수 이다.
※ α (가중치):일반적으로 1.0을 사용하지만, 스레드 수가 부족하여 에러가 발생한다면 1.5~2.0 정도로 설정한다. 다른 JBoss EAP 6 인스턴스가 다운됐을 경우에서도, 모든 웹 서버로부터의 요청을 처리할 수 있도록, 웹 서버의 대수를 고려해야 한다. 또한, 스레드의 생성과 소멸은 비동기이기 때문에 가중치의 조정이 필요하다.
HTTP/AJP 커넥션에 대한 스레드 풀 설정
HTTP/AJP 커넥터에 대한 스레드 풀의 최대값을 충분히 설정해야 한다.
<connector name="ajp" protocol="AJP/1.3" scheme="http" socket-binding="ajp" executor="http-thread-pool" max-connections="3260"/>
<connector name="http" protocol="HTTP/1.1" scheme="http" socketbinding="http" enable-lookups="false" executor="http-thread-pool" max-connections="3260"/>
<subsystem xmlns="urn:jboss:domain:threads:1.1">
… 생략 …
<unbounded-queue-thread-pool name="http-thread-pool">
<max-threads count="250"/>
<keepalive-time time="60" unit="minutes"/>
</unbounded-queue-thread-pool>
… 생략 …
</subsystem>
커넥션 타임아웃
로드 밸런스등의 용도로 mod_jk, mod_proxy, mod_cluster 등의 모듈을 사용하기 위해 Apache를 JBoss EAP 6 앞에 배치했을 경우, Apache 웹 서버와 JBoss EAP 6간의 연결은 일정 시간 연결된 상태를 유지할 수 있도록 한다.
지정한 시간 동안 요청이 없는 경우 타임아웃으로 끊어져 할당되어 있던 워커 스레드는 스레드 풀에 반환한다.
연결 타임아웃은 시스템 프로퍼티로 지정한다. AJP 및 HTTP 각각의 커넥터의 connection 타임아웃 지정은 다음과 같은 시스템 프로퍼티를 사용하여 설정한다.
- AJP/1. 3 커넥터
| 커넥터 | 설명 |
|---|---|
| org.apache.coyote.ajp.DEFAULT_CONNECTION_TIMEOUT | 연결요청에 대해 응답이 없는 경우 대기 시간을 밀리 세컨드로 지정한다. 기본값은 -1(무제한)이어서 요청이 없어도 connection은 끊어지지 않는다. |
- HTTP/1. 1 커넥터
| 커넥터 | 설명 |
|---|---|
| org.apache.coyote.http11.DEFAULT_CONNECTION_TIMEOUT | 연결 요청에 대해 응답이 없는 경우 대기 시간을 밀리 세컨드로 지정한다. 기본값은 60000 밀리 세컨드(60 초)이다. |
커넥션 타임아웃 튜닝
Apache 웹 서버와 JBoss EAP 6간에 유지되는 연결은 아파치 웹 서버에 대한 타임아웃 설정이 있으며 지정한 시간 동안 응답이 없으면 웹 서버에서 연결이 끊어지게 된다.
다음은 시스템 프로퍼티 org.apache.coyote.ajp.DEFAULT_CONNECTION_TIMEOUT값을 60000 으로 설정하는 예이다.
또, mod_jk, mod_cluster(mod_proxy)의 connection 타임아웃 설정의 항목명은 다음 표와 같다.
| 커넥터 | 타임아웃 속성 | 설명 |
|---|---|---|
| mod_jk | connection_pool_timeout | JBoss 대기 상태 connection의 타임아웃 시간(초) 지정한다. |
| mod_cluster (mod_proxy) | ttl | JBoss와 연결 대기 상태 타임아웃 시간(초). |
표 5. 웹 커넥터의 타임아웃 설정