티스토리 뷰
본 포스팅에서는 JBoss Thread Pool 설정에 대해 알아보겠습니다.
Thread Pool의 장단점에 대해 알아보고, Jboss 6 버전을 기반으로 제공하고 있는 다양한 Thread Pool 설정방식에 대해 살펴보겠습니다.
먼저 Thread Pool은 왜 사용해야할까요?
Thread pool이라는것은 일정 갯수의 Thread를 두고, 만약 작업할 일이 생긴다면 대기상태인 Thread가 있는지 보고, 있다면 그 쓰레드로 작업을 처리한 후 다시 대기상태로 전환할수 있도록 만들어놓은 영역이라고 생각하면 됩니다.
JAVA에서 Thread pool을 사용하는 이유는, 아래와 같습니다.
- 성능저하를 방지하기 위해 : 매번 발생되는 작업을 동시에 병렬적으로 처리하기 위해서는 Thread를 생성하고 / 수거해야하는데 이 작업은 프로그램의 전체적인 퍼포먼스를 저하시킵니다. 따라서 Thread를 재활용할 수 있다는 게 Thread pool을 사용하는 가장 큰 목적입니다.
- 다수의 사용자 요청을 처리하기 위해 : 서비스 측면에서 바라볼때, 다수의 사용자의 요청을 빠르게 수용하고 처리하여 대응하기 위한 준비라고 할수 있습니다 .
그럼 Thread pool의 단점은 무엇일까요?
- 메모리 낭비를 초래할 수 있습니다 : 만약 많은 병렬 처리를 위해서 1억개의 Thread를 만들어 놓은 상태에서, 100개의 요청이 들어왔다면, 나머지 Thread는 아무일도 하지않고 메모리만 차지하는 경우가 발생될 수 있습니다.
그럼 JBoss 에서 제공하는 Thread pool 방식에 대해서 알아보겠습니다.
1) unbounded-queue-thread-pool
2) bounded-queue-thread-pool
3) blocking-bounded-queue-thread-pool
4) queueless-thread-pool
5) blocking-queueless-thread-pool
6) scheduled-thread-pool
1)unbounded-queue-thread-pool방식은 모든 요청에 대해 처리하고, 만약 max thread count를 초과할 경우 unbounded FIFO queue에서 대기하도록 하는 방식입니다.
<subsystem xmlns="urn:jboss:domain:threads:1.1"> <unbounded-queue-thread-pool name="ThreadPool"> <max-threads count="10" /> <keepalive-time time="30" unit="seconds"/> </unbounded-queue-thread-pool> </subsystem> |
처리를 보장한다는 점에서 장점이 될 수 있으나 요청이 메모리에 계속 쌓이기때문에 OOME가 발생할 수 있습니다.
2)bounded-queue-thread-pool 방식의 경우는 초과되는 요청에 대해서 처리가 가능해질 때까지 요청을 reject(또는 discard)하는 모델입니다.
<subsystem xmlns="urn:jboss:domain:threads:1.1"> <bounded-queue-thread-pool name="ThreadPool" blocking="true"> <queue-length count="500"/> <core-threads count="5"/> <max-threads count="10"/> <keepalive-time time="30" unit="seconds"/> </bounded-queue-thread-pool> </subsystem> |
max-threads의 값을 500으로 했을 시 core-threads는 500 보다 작거나 같아야 합니다. core-threads를 지정하지 않는다면 내부적으로 max-threads와 같은 500으로 지정됩니다. 다만 core-threads 기본적으로 요청 처리 실행을 준비하고 있는 준비된 스레드입니다. queue-length 는 부하가 일시적으로 높아졌을 경우 버퍼링 기능을 하는 것으로써 적정한 수치는 비즈니스 요건과 애플리케이션 및 환경마다 다를 수 있으므로 비즈니스 시나리오에 기반한 성능 테스트 후 성능이 가능 높게 나오는 수치로 결정하셔야 합니다.
core-threads 는 스레드 풀에서 실행을 준비하고 있는 기본 스레드 개수를 입니다. core-threads 값을 지정하지 않으면 max-threads(maximum thread pool size)의 값과 동일하게 지정됩니다. core-threads 수치가 너무 크면 사용되지 않는 스레드가 발생하여 리소스 낭비가 발생하게 됩니다. 수치가 너무 낮으면 thread 동시 실행성이 줄어들게 됩니다.
queue-length 는 실행가능한 스레드가 주어질 때까지 요청들이 대기하는 큐의 사이즈입니다. 사이즈가 클수록 더 많은 메모리가 사용되고 큐가 채워지지 않는 만큼 리소스 낭비가 됩니다.
3)blocking-bounded-queue-thread-pool 방식은 구조적으로 초과되는 요청에 대해서 처리가 가능해질 때까지 block 시키는 모델입니다. 초과되는 요청을 버리(discard)거나 reject하지 않는 모델입니다.
<subsystem xmlns="urn:jboss:domain:threads:1.1"> <blocking-bounded-queue-thread-pool name="MyScheduler"> <max-threads count="1" /> </blocking-bounded-queue-thread-pool> </subsystem> |
4)queueless-thread-pool은 독립적인 thread들이 수행될때 사용하는 단순한 thread pool입니다. 또한 long-running task에 적합한 방식입니다. 대기할 수 있는 thread의 queue가 존재하지않으며, max-thread를 초과할경우 reject하게 됩니다.
<subsystem xmlns="urn:jboss:domain:threads:1.1"> <queueless-thread-pool name="MyExecutor"> <max-threads count="200" /> </queueless-thread-pool> </subsystem> |
5)blocking-queueless-thread-pool 방식은 대기할수 있는 thread 의 queue가 존재하지않으며, 만약 max thread count를 초과할경우 block상태로 유지되며, 수행중인 thread가 완료될때까지 처리하지않습니다.
<subsystem xmlns="urn:jboss:domain:threads:1.1"> <blocking-queueless-thread-pool name="MyScheduler"> <max-threads count="1" /> </blocking-queueless-thread-pool> </subsystem> |
6)scheduled-thread-pool 방식은 java.util.concurrent.ScheduledThreadPoolExecutor class에 기반하여, 특정 시간과 특정 간격에 수행될 필요성이 있는 thread를 처리하는 방식입니다. 명령을 주기적으로 수행할 수 있는 thread pool을 생성해야 할 시 사용하는 모델이라고 볼 수 있습니다.
<subsystem xmlns="urn:jboss:domain:threads:1.1"> <scheduled-thread-pool name="MyScheduler"> <max-threads count="1" /> </scheduled-thread-pool> </subsystem> |
그럼 이제 주요 방식인 4가지를 테스트해보겠습니다.
1) unbounded-queue-thread-pool
2) bounded-queue-thread-pool
3) blocking-bounded-queue-thread-pool
4) queueless-thread-pool
<subsystem xmlns="urn:jboss:domain:threads:1.1"> <unbounded-queue-thread-pool name="ajp-thread-pool-1"> <max-threads count="10"/> <keepalive-time time="30" unit="seconds"/> </unbounded-queue-thread-pool> <bounded-queue-thread-pool name="ajp-thread-pool-2"> <core-threads count="5"/> <queue-length count="5"/> <max-threads count="10"/> </bounded-queue-thread-pool> <blocking-bounded-queue-thread-pool name="ajp-thread-pool-3" allow-core-timeout="true"> <core-threads count="2"/> <queue-length count="2"/> <max-threads count="1"/> <keepalive-time time="60" unit="seconds"/> </blocking-bounded-queue-thread-pool> <queueless-thread-pool name="ajp-thread-pool-4"> <max-threads count="10"/> <keepalive-time time="10" unit="seconds"/> </queueless-thread-pool> </subsystem> .. <subsystem xmlns="urn:jboss:domain:web:2.2" default-virtual-server="default-host" native="false"> <connector name="ajp" protocol="AJP/1.3" scheme="http" socket-binding="ajp" executor="[thread pool name]"/> ... |
- JMeter Thread Properties
Number of Threads(Users):10
Ram-Up Period(in seconds):6
Loop Count:20
호출도메인 : http://IP/jbtest/sleep.jsp (5초간의 sleep time을 갖는 jsp파일)
1) unbounded-queue-thread-pool
./jboss-cli.sh --controller=172.21.70.24:10049 --connect --command="/subsystem=threads/unbounded-queue-thread-pool=ajp-thread-pool-1:read-resource(include-runtime=true)" { "outcome" => "success", "result" => { "active-count" => 10, "completed-task-count" => 0L, "current-thread-count" => 10, "keepalive-time" => { "time" => 30L, "unit" => "SECONDS" }, "largest-thread-count" => 10, "max-threads" => 10, "name" => "ajp-thread-pool-1", "queue-size" => 25, "rejected-count" => 0, "task-count" => 12L, "thread-factory" => undefined } } |
- current-thread-count : 현재 max thread count까지 사용되고있는것을 확인할 수 있습니다.
- queue-size : FIFO queue가 계속 늘어나며, block상태의 thread가 점점 늘어나지만, reject되는 thread는 없음을 알 수 있습니다. block이 될경우, java.net.SocketException: Socket closed(response data를 받습니다.
2) bounded-queue-thread-pool
./jboss-cli.sh --controller=172.21.70.24:10049 --connect --command="/subsystem=threads/bounded-queue-thread-pool=ajp-thread-pool-2:read-resource(include-runtime=true)" { "outcome" => "success", "result" => { "allow-core-timeout" => false, "core-threads" => 5, "current-thread-count" => 10, "handoff-executor" => undefined, "keepalive-time" => undefined, "largest-thread-count" => 10, "max-threads" => 10, "name" => "ajp-thread-pool-2", "queue-length" => 5, "queue-size" => 5, "rejected-count" => 18, "thread-factory" => undefined } } |
- current-thread-count : 현재 max thread count까지 사용되고있는것을 확인할 수 있습니다.
- queue-size : 설정한 queue-length만큼 queue가 쌓인것을 확인할 수 있습니다.
- rejected-count : queue에서 처리가능한 thread를 제외하고 reject된 count수입니다. 점점 늘어나고 있는것을 확인할 수 있었습니다.
reject될시, 502 Bad Gateway(response data)를 받게됩니다.
따라서, max thread count가 찬 이후에는 queue-length만큼 thread가 대기하게 되며, 이후 요청은 reject됨을 알 수 있습니다.
*추가적으로 bounded-queue-thread-pool 에서는 handoff executor를 설정할 수 있습니다.
메인 ajp-thread-pool의 queue까지 가득찰 경우, handoff executor를 지정하게되면 또 하나의 thread pool을 사용할 수 있습니다.
하지만 이 추가적인 thread pool의 queue까지 가득찰 경우, 이후 요청은 모두 reject됩니다.
<bounded-queue-thread-pool name="ajp-thread-pool-2"> <core-threads count="5"/> <queue-length count="5"/> <max-threads count="10"/> <handoff-executor name="handoff-ajp-thread-pool-2"/> </bounded-queue-thread-pool> <bounded-queue-thread-pool name="handoff-ajp-thread-pool-2"> <core-threads count="5"/> <queue-length count="5"/> <max-threads count="10"/> </bounded-queue-thread-pool> |
3) blocking-bounded-queue-thread-pool
./jboss-cli.sh --controller=172.21.70.24:10049 --connect --command="/subsystem=threads/blocking-bounded-queue-thread-pool=ajp-thread-pool-3:read-resource(include-runtime=true)" { "outcome" => "success", "result" => { "allow-core-timeout" => true, "core-threads" => 5, "current-thread-count" => 10, "keepalive-time" => { "time" => 10L, "unit" => "SECONDS" }, "largest-thread-count" => 10, "max-threads" => 10, "name" => "ajp-thread-pool-3", "queue-length" => 5, "queue-size" => 5, "rejected-count" => 0, "thread-factory" => undefined } } |
- current-thread-count : 현재 max thread count까지 사용되고있는것을 확인할 수 있습니다.
- queue-size : queue는 모두 사용되는 상태로, block상태의 thread가 점점 늘어나지만, reject되는 thread는 없음을 알 수 있습니다.block이 될경우, java.net.SocketException: Socket closed(response data를 받습니다.
4)queueless-thread-pool
./jboss-cli.sh --controller=172.21.70.24:10049 --connect --command="/subsystem=threads/queueless-thread-pool=ajp-thread-pool-4:read-resource(include-runtime=true)" { "outcome" => "success", "result" => { "current-thread-count" => 10, "handoff-executor" => undefined, "keepalive-time" => { "time" => 10L, "unit" => "SECONDS" }, "largest-thread-count" => 10, "max-threads" => 10, "name" => "ajp-thread-pool-4", "queue-size" => 58, "rejected-count" => 58, "thread-factory" => undefined } } |
- current-thread-count : 현재 max thread count까지 사용되고있는것을 확인할 수 있습니다.
- queue-size/rejected-count: 동일하게 증가되는 현상을 확인할 수 있었습니다. 해당 부분은 jboss 6.4r기준 queue-size 에 대한 bug로, queue-size는 count되지않고 reject-count만 count되도록 patch를 진행중에 있습니다.따라서, queue-size는 무시하고 reject-count만 확인하면 됩니다.해당 방식은 단순 long running task를 위한 방식이기때문에, 충분한 max thread count를 산정하는것이 좋을것으로 판단됩니다.
결론입니다.
1)queue만 있는경우
queue에 계속 쌓임>OOME발생가능성 높음
2)queue가 있고, non-block인 경우
max-thread를 초과>queue에 담김>queue초과>reject
3)queue가 있고, block인경우
max-thread를 초과>queue에 담김>queue초과>block(thread lock)
4)queue가 없고, non-block인 경우
max-thread를 초과>reject
|
unbounded |
bounded(non-blocking) |
blocking-bounded |
queueless |
queue 유무 |
O(FIFO) |
O |
O |
X |
reject 유무 |
X |
O |
X |
O |
이상으로 Thread pool 방식에 대해 알아보았습니다.
고맙습니다.
'④ 미들웨어 > ⓦ WildFly' 카테고리의 다른 글
[WildFly] 외부캐시 서버와의 연동을 통한 세션 클러스터링 테스트 (0) | 2018.10.24 |
---|---|
[WildFly] database연동방식 (Thin,OCI) (0) | 2018.09.27 |
[WildFly] 운영자 검검 리스트 (0) | 2018.08.21 |
[WildFly] 하나의 Standalone Server에서 동일 Context 사용 방법 (0) | 2018.08.10 |
[WildFly] 통합 개발 환경 구성하기(Eclipse Plug-In 가이드) (0) | 2018.08.07 |
- Total
- Today
- Yesterday
- JBoss
- k8s
- SWA
- TA
- wildfly
- node.js
- Da
- Docker
- SA
- 마이크로서비스
- webtob
- openstack tenant
- aws
- 마이크로서비스 아키텍처
- OpenStack
- 아키텍처
- 쿠버네티스
- apache
- API Gateway
- MSA
- jeus
- git
- JEUS6
- JEUS7
- aa
- 오픈스택
- Architecture
- kubernetes
- nodejs
- openstack token issue
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |