티스토리 뷰

보통 WAS는 Thread를 기반으로 하여 작동한다. 사용자가 서비스를 요청하면 이 요청은 네트워크를 타고 APM의 Request Processor에게 전달된다. Request Process는 이 요청을 Execution Queue에 적재하고 가용한 Thread를 할당 받아 Application에 접근하여 응답을 받아낸다. Thread를 할당 받으면 이 요청은 Execution Queue에서 제거 된다. Execution Queue는 결국 Thread를 할당 받기 위한 대기열인 셈이고 Thread를 할당 받는 것이 Execution을 하는 것으로 생각할 수 있다.

Thread Pool은 이 Thread를 모아 놓은 곳으로 이해 할 수 있다. Thread를 생성하고 제거하는 것이 Resource를 많이 사용하는 작업이기 때문에 미리 Thread를 생성해 놓고 요청이 있을 때 가용한 Thread를 할당 받아 사용하고 작업이 끝난 후에 Thread를 다시 Thread Pool에 반환하는 작업을 하게 된다.

이 Thread Pool의 설정은 성능에 큰 영향을 준다. Pool의 크기가 너무 작으면 요청은 Execution Queue에서 대기하는 시간이 길어질 것이고 Pool의 크기가 너무 큰 경우는 CPU가 다수의 Thread사이에서 context switch를 하는데 많은 시간을 소모하게 되기 때문이다.

그렇다면 WAS를 모니터링 할 때 Thread Pool의 문제를 어떻게 인지할 것인가? 일단 Thread Pool의 크기가 너무 작으면 Thread Pool은 100%의 사용량을 보여 줄 것이고 그로 인해 대부분의 요청들은 Pending상태로 머물러 있는 시간이 길어질 것이다. 결국 응답시간이 길어진다. 이 경우 CPU의 점유율은 크게 증가하지 않는다고 하는데 그 이유가 CPU를 과다하게 사용할 만큼의 요청이 들어가지 않기 때문이라고 한다.이와는 반대로 Thread Pool이 너무 큰 경우 CPU의 사용률이 아주 높다고 한다. 그 이유는 앞서 언급한 바와 같이 context Switch의 문제도 있고 또한 Garbage Collection이 수반되기 때문이라고 한다.

그렇다면 단순하게 CPU와 Thread Pool의 사용형태로 이러한 Thread Pool에서 야기되는 성능 문제를 직관적으로 파악할 수 있을까? 이를 위해 테스트를 해보기로 했다. 테스트의 시나리오는 Thread pool에100개의 thread설정해 놓고 아래와 같이 단순한 Text결과를 가져오도록 하는 Application을 만들었다. 이 Application을 1000개의 Thread를 통해 수행해 보면 100개의 Thread 를 모두 사용할 것은 물론이고 CPU도 그렇게 많이 사용하지 않을 것이라는 예상이 가능했다.

<html>
<head>
<base href="<%=basePath%>">
<title>My JSP 'Text.jsp' starting page</title>
</head>
<body>
This is my JSP page. <br>
</body>
</
html>

 

그러나 예상은 빗나가고 말았다. 아래의 그림은 Thread를 모니터링 한 결과이다. 동시에 100개는 고사하고 20개도 사용한 적이 없을 뿐더러 CPU는 98~99%를 사용하였다.

그렇다면 이 100개의 Thread는 과연 무슨 일을 하고 있었을까? 각 Thread별로 모니터링을 한 그래프를 살펴보도록 하자.

이 그래프에서 보면 4가지 색으로 구성되어 있는 것을 알 수 있다. 물론 이것은 툴에서 정한 것이다. 일단 녹색은 thread이 runnable인 상태를 의미한다. 주황색은 Waiting상태를 의미한다. Waiting이란 현재 timer나 다른 Thread에 의해 깨어날 때까지 idle하게 대기하고 있는 상태를 의미한다. 붉은 색은 Thread가 blocking되어 있는 상태를 의미하는데 이는 다른 thread에 의해 동기화 작업을 수행하는 것이라고 한다. 하늘색은 Net I/O상태를 의미한다. 이 thread는 socket을 통해 데이터를 읽거나 쓰는 작업 또는 요청을 받고 있는 경우를 말한다.

위의 그래프를 보면 runnable 상태보다는 blocking상태로 가장 많은 시간을 사용했음을 알 수 있다. 그렇다면 이것이 CPU를 과다하게 사용하는 것과 관련이 있는 것일까? blocking상태는 단순한 Waiting은 아니다. 그렇다면 Blocking상태는 어떤 작업을 의미하는 것일까?

앞서 thread의 blocking은 동기화(synchronization) 에 관한 자료를 보면 Synchronization에 대한 여러 가지 용어를 찾을 수 있다. 그 중 multi-thread환경에서 사용될 만한 몇 가지를 열거해 보면 다음과 같다.
1) Lock : 이 용어는 Synchronization code나 method를 수행하는 특정 thread의 권한이라고 생각할 수 있는데 Synchronization code를 수행하는 것을 lock을 소유한다고 표현할 수 있다.

2) condition variable : 공유 데이터에 대한 특정 조건에 따라 thread의 실행을 중지하거나 다시 실행시키는 역할을 하는 동기화 장치로 condition variable 라는 특별한 변수를 만들고 thread는 이 변수가 신호를 받을 때까지 기다린다. 다른 쓰레드에서 특정조건이 만족되었을 경우 condition variable 에 시그널을 보낸다. 그러면 condition variable 가 신호를 받기를 기다리던 thread가 깨어나서 동작하는 방식으로 실제적인 lock과는 거리가 있다. condition variable 는 한 thread가 condition variable 를 기다릴 준비를 하는 동안 (실제로 대기하기 전에) 다른 thread가 해당 condition variable 에 시그널을 보내는 것과 같은 경쟁 상태를 방지하기 위해 항상 뮤텍스와 함께 사용해야 한다.

3) Monitor : 이것은 가장 일반적인 Synchronization 용어임에도 일관되게 사용되지 않는 것이기도 하다. 어떤 시스템에서는 monitor를 simple lock으로 사용하고 어느 곳에서는 wait와 notify 메커니즘과 유사하게 사용하기도 한다.

그러나 이 Synchronization 용어들 중 어떤 것이 위의 상황을 설명할 수 있을까? 즉 단순한 Application을 수행하는 데 있어 thread Pool이 서로 blocking되는 것을 어떻게 설명할 수 있을까? 100개의 Thread를 만들어 놓고 30개도 못쓴다는 것은 문제가 있어 보인다. 그러면 Request수를 줄여야 할 까 아니면 다른 식의 튜닝이 가능할까? 여러 가지 의문이 뒤따른다.

그래서 이번에는 이러한 의문만을 남기고 다음 기회에 thread의 Synchronization에 대해서 좀 더 알아보기로 하자.

 

참고자료

Java Thread, Scott Oaks, Henry Wong, Mike Loukides , O'Reilly, 1998

Pro Java EE 5 Performance Management and Optimization, Steven Haines, Apress, 2006

댓글
댓글쓰기 폼