4,552,072 th visitor since 2017.2.1 ( Today : 657 )
Programming
No. 682
Name. swindler
Subject. WAS Thread수와 Instance수의 산정
Main Cate. Java
Sub Cate.
Date. 2010-08-18 14:02
Hit. 4113 (211.36.27.8)
File.
WAS의 Thread수를 결정 하는 방법에 대해서 알아보도록 하자.
성능 산정에 대한 몇가지 기본 Parameter에 대해서 정의하고 시작하도록 하자.


1. Response Time & Think Time




<그림 1. Response Time & Think Time>


일반적인 웹시스템에 대해서 생각해보자.

ERP 시스템에 로그인하는 프로세스를 가정해보면, “사용자는 해당 폼에 아이디와 비밀번호를 넣고, 로그인을 클릭한다. 아이디와 비밀번호는 네트워크를 통해서 서버에 전송되고, 서버에서는 이 아이디와 패스워드를 이용해서 사용자 정보를 조회한후 각 사용자에게 알맞는 페이지를 다시 네트워크를 통해서 사용자에게 전송할 것이다.
사용자는 전송되어 있는 페이지를 읽어보고, 공지사항을 클릭한다.”

아주 일반적인 웹시스템의 플로우이다. 여기서 몇가지 용어를 정의하자.

Response Time
사용자가 request한 시점 (즉 Click한 시점)에서 시스템이 Response를 할때까지의 시간을 정의한다. 이 시간에는 사용자의 요청이 서버 시스템으로 전송되는 네트워크 시간(Request Network Time) 과, 서버에서 생성된 응답 내용이 네트워크를 통해서 사용자에게 전송되는 시간(Response Network Time)을 포함한다.
Reponse Time중에서 실제로 서버가 부하를 받는 시간은 Transaction Time이라고 정의하자

Think Time
사용자에게 전달된 정보는 사용자가 그 내용을 인지하고 다음 동작을 취할때까지의 생각하는 시간이 필요하다. 이를 Think Time이라고 정의하자.

Request Interval
Response Time과 Think Time 이둘을 합친것을 Request Interval이라고 정의를 한다. 이 시간은 사용자가 클릭을 해서 시스템으로부터 정보를 받아서 인지할때 까지의 시간을 나타낸다.

Visit Time
하나의 사용자에 대해서 Request Interval의 집합을 Visit Time이라고 정의한다. 이 값은 사용자가 시스템에 접속해서 사용을한시간을 정의한다.
※ 상기의 값들은 access.log 파일을 웹마이닝툴을 이용해서 분석하면 그 값을 추출해낼 수 있다.


2. Concurrent User (동시 사용자수)


동시 사용자의 개념은 매우 모호하다. 웹 시스템의 경우에는 Session 유지 시간이 일반적으로 30분이기 때문에, 10분전에 사용을 끝낸 사용자도 세션은 유지 되기 때문에, 시스템에 영향을 준다. 반대로 시스템을 사용중인 사용자라도 response를 받은 후에 정보를 읽고 있는 Think time 중에 있다면 시스템에 영향을 주지 않는다. MainFrame이나 C/S 시스템과 다르게 웹시스템에서의 동시 사용자의 개념은 매우 모호하다.



<그림 2. 동시 사용자>


그렇다면 우리는 동시 사용자의 개념을 어떻게 정의할것인가?
동시 사용자를 지금 시스템을 사용하기 위해서, 단말기 앞에 앉아있는 사람 즉 “ 동시 단말 사용자” 라고 정의하자.
이 숫자는 단위시간 동안에, 현재 사이트를 방문하고 있는 사용자수로 판단할 수 있으며, 그림 2에서는 10분간의 동시 사용자는 3명이 된다.

이 단위 시간을 정의하는것이 중요한데, 보통 10분단위정도의 단위로 측정을 하며, 측정 시간대에 따라서 시스템의 동시 사용자수가 차이가 난다. (PEAK TIME이냐? 아니면 작업이 없는 오후 시간이냐)

시스템의 동시 사용자를 측정하기 위해서는 access.log를 이용해서 각 시간대별로 동시 사용자를 파악하는게 중요하고, 특히 최대 동시 사용자수를 구하는 것이 시스템 용량 산정에 있어서 가장 중요한 요인이 된다.


3. Active User


앞에서 산출한 동시 사용자 모두가 시스템에 영향을 주는것이 아니다. 어떤 사용자는 시스템에서 나온 정보를 읽고 인지하는 과정 (Think Time)에 있는 사용자도 있을 수 있을 것이다. 이런 사용자를 제외하고, 실제로 현재 시간에 시스템을 사용하고, 시스템에서 트렌젝션을 수행하고 있는 사용자를 Active User라고 정의하자.



<그림 3. Active User>


Active User는 실제 시스템에서 Transaction을 수행하고 있는 User수이기 때문에, 실제로 서버에서 Thread가 돌고 있는것으로 판단할 수 있기 때문에, 이 Active User의 수가, 즉 서버의 Working Thread수가 된다.


4. TPS (Throughput Per Second)


TPS는 초당 시스템의 처리량 (Throughput)을 나타낸다.





TPS = Throuhgput / measured time [seconds]
    = number of response / measured time [seconds]
    = number of request / measured time [seconds]

시스템의 처리량을 측정 시간으로 나누면 되는데, access.log에서의 경우에는 response의 수는 정확하게 기록하기가 어렵기 때문에, 해당 서비스에 대한 request수를 시간으로 나눈것을 TPS로 계산한다.

WAS의 경우에는 이 Throughput 값을 모니터링을 가능한데.
웹로직의 경우에는 여러 방법을 이용해서 TPS를 측정할 수 있다.

1) 웹로직 Perfomance Monitoring을 통한 실시간 TPS 값 모니터링



<그림 4. 웹로직 콘솔을 통한 실시간 TPS 모니터링>


웹로직 콘솔에서 “ 도메인 > Servers > 해당 서버 > Monitoring > Performance “ 를 선택하면 Throughput 부분에서 실시간으로 TPS를 확인할 수 있다.

2) 웹로직 Queue의 Throughput 값 모니터링을 통한 누적 Throughput 값의 모니터링



<그림 5. 웹로직 콘솔을 통한 웹로직 Throughput 누적량 모니터링>


웹로직 콘솔에서 “도메인 > Servers > 해당 서버 > Monitoring > General > Monitor All Active Queues “ 를 선택하면 <그림5>에서와 같이 각각의 웹로직 큐별 누적 Throughput 값을 볼 수 있다. 이 값은 각 큐별로 웹로직이 부팅되면서 부터의 누적 Throughput값이며, 사용자 큐가 분리되었을 경우에는 각 큐별 Throughput을 합산해야 한다.

일반적인 경우에는 “default” 큐 하나만 생성이 되어 있으며 _weblogix_xxx 로 명명된 큐들은 시스템이나 관리자가 사용하도록 배정된 큐이기 때문에, 별도로 체크하지 않아도 된다.

3) MBean을 통한 Throughput 값의 수집

앞에서 얻은 누적 Throughput 값을 JMX 인터페이스를 통한 Mbean을 이용해서 프로그램으로 가지고 올 수 있다. 1초 마다 누적 Throughput 값을 읽어오면, 그값을 통해서 실시간으로 TPS값을 알 수 있고, 그 값을 가지고 통계를 내면 해당 시스템의 평균 TPS와 PEAK TIME에서의 TPS 값을 쉽게 산정할 수 있다.

다음 <소스 1.> 은 웹로직 기반에서 Throughput을 읽어오도록 되어 있는 소스 이다.





import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.AuthenticationException;
import javax.naming.CommunicationException;
import javax.naming.NamingException;
import weblogic.jndi.Environment;
import weblogic.management.MBeanHome;
import weblogic.management.WebLogicMBean;
import weblogic.management.runtime.*;
import java.util.*;

public class TPS{

  // getMBeanHome
  // MBean을 가져오기 위한 Home Interface를 JNDI lookup을
  // 통해서 retrieve한다.
  public MBeanHome getMBeanHome(String url
      ,String username
      ,String password
      ,String servername)
    throws Exception
  {
    Hashtable env = new Hashtable();
    env.put(Context.INITIAL_CONTEXT_FACTORY,
      "weblogic.jndi.WLInitialContextFactory");
    env.put(Context.PROVIDER_URL,url);
    env.put(Context.SECURITY_PRINCIPAL,username);
    env.put(Context.SECURITY_CREDENTIALS,password);

    InitialContext ctx = new InitialContext(env);

    // get Mbean Home
    return (MBeanHome)ctx.lookup(MBeanHome.JNDI_NAME+"."+servername);
  }// getMBean Home

  // getRuntimeMBean
  // 해당 RuntimeMBean을 가지고 온다.
  public WebLogicMBean getRuntimeMBean(MBeanHome home,
      String name,String mbeanname)
    throws Exception
  {
    WebLogicMBean mbean = home.getRuntimeMBean(name,mbeanname);
    return mbean;
  }


  // main
  public static void main(String args[]){
    TPS info = new TPS();
    System.out.println("connection :"+args[0]);
    System.out.println("server name :"+args[1]);
    String servername=args[1];

    try{
    MBeanHome home = info.getMBeanHome(args[0],"system","weblogic"
      ,servername);
    System.out.println("home is :"+home);
    JVMRuntimeMBean jvmMbean = (JVMRuntimeMBean)info.getRuntimeMBean(home,servername,"JVMRuntime");
    ServerRuntimeMBean serverMbean = (ServerRuntimeMBean)info.getRuntimeMBean(home,servername,"ServerRuntime");

    // for 8.1
    //ExecuteQueueRuntimeMBean queueMbean = (ExecuteQueueRuntimeMBean)info.getRuntimeMBean(home,"weblogic.kernel.Default","ExecuteQueueRuntime");

    // for 6.1
    ExecuteQueueRuntimeMBean queueMbean = (ExecuteQueueRuntimeMBean)info.getRuntimeMBean(home,"default","ExecuteQueueRuntime");

    int cnt=0;
    int cur_throughput=0,last_throughput=0,total_throughput=0;

    while(true){

      if(cnt++ ==0 ){
        System.out.println("server :"+args[1]);
        System.out.println("\ndate| tp ");
      }
      if(cnt > 10) cnt=0;
      total_throughput = queueMbean.getServicedRequestTotalCount();
      cur_throughput = total_throughput - last_throughput;
      last_throughput = total_throughput;

      System.out.print("["+(new Date())+"]\t");
      System.out.println(cur_throughput+"/"+total_throughput+" "); // througthput

    Thread.sleep(1000);
    }// while
    }catch(Exception e){
      System.out.println(e.toString());
    }
  }
}

<소스 1. TPS.java>



5. WAS의 Working Thread수의 산정


앞에서 성능에 관련된 여러 변수에 대해서 정의를 했다.
그럼 이 변수들을 이용해서 WAS의 Working Thread수를 구해보도록 하자.

Working Thread = Active User 이다. 즉 우리는 Active User의 수를 구하면 되는데
TPS산정 공식에서 TPS = (Active User) / (Average Response Time) 이기 때문에,





공식 1: Active User = TPS * (Average Response Time)이 된다.

TPS = Concurrent User / Request Interval 과 같다.
이값을 공식 1에 대입하면





공식 2 : Active User = (Concurrent User ) * (Average Response Time) / (Request Interval)

이 되고, Request Interval = Response Time + Think Time이기 때문에, 이를 공식 2에 다시 대입하면





공식 3 : Active User = (Concurrent User) * (Average Response Time ) / (Average Response Time + Average Think Time)

공식 3을 이용하면, 개발하고자 하는 목표 시스템의 “목표 동시 사용자수” (Concurrent User), “목표 응답 시간”(Average Response Time)과 “목표되는 평균 Think Time”(Average Think Time)을 산정하면 시스템이 실제로 사용해야하는 Thread수를 산정할 수 있다.

예를 들어, 동시에 300명이 사용하고, 응답시간은 3초이내이며 Think Time이 15초인 인터넷 시스템이 있다면, 이 시스템의 Activer User = 300*3/(3+15) = 50 명으로 해당 시스템의 총 Thread수는 50개가 적정이 된다.


6. WAS의 Thread 수 검증과 테스트


앞에서 제시한 공식 3은 보기에는 완벽해 보이는 공식이지만, 실세계에서 개발 운용되는 애플리케이션은 불행이도 우리의 예상을 벗어나는 경우가 많다.

평균 응답시간과 평균 Think Time은 어디까지나 평균 값일 뿐이며 애플리케이션의 구조에 따라서 PEAK TIME에서의 그 값이 현저하게 차이가 날 수 있고, 그 결과로 해당 THREAD가 모자르는 현상을 초래할 수 있다. 즉 공식 3에서 얻어진 값은 Working THREAD는 최소 값 내지는 Tuning에 있어서 기본값으로 판단하는 것이 현명하다.

이 값을 기본으로 Working Thread의 최적의 숫자를 산정해야하는데 이는 스트레스트 테스트와 운영중의 로그를 기반으로 결정하게 된다.


7. 웹로직의 적정 쓰레드 수






WebLogic HW의 구성


웹로직을 설치할 하드웨어 구성에 대해서 고민해보자, 구성 방법에 대해서는 수평적 확장과 수직적 확장 두가지를 생각해볼 수 있는데.
수직적 확장이란 웹로직이 구동되고 있는 HW BOX 자체의 CPU의 성능을 올려주는 방법을 이야기 하며, 수평적 확장이란 HW BOX를 늘려서 각 BOX를 클러스터링으로 묶어주는 구성 방법을 이야기한다.

BEA 의 MedRec Application을 이용한 테스트 결과를 살펴보기로 하자.
이 테스트 결과는 WLS8.1을 이용해서, 최적화된 상태로 진행이 되었으며, HW 1BOX에 하나의 인스턴스만 작동하는 환경에서 실행 되었다.

그림 6은 Solaris에서 400Mhz 4CPU BOX를 하나씩 늘려가면서 TPS를 측정한 그래프이다. BOX를 증가할때 마다 100TPS씩 비례적으로 늘어나는것을 볼 수 있다.



<그림 6. 웹로직의 수평적 확장>


반면 그림 7의 경우에는 750MHz CPU를 1개에서 2배씩 1,2,4,8식으로 늘릴때 마다, 성능이 2배씩 증가하는것이 아니라 약 1.8배씩 증가하는것을 볼 수 있다.

MedRec Application + WebLogic 8.1 + Solaris 의 경우에는 수직적 확장 보다 수평적 확장이 성능 향상에 더 기여를 한다는것을 수치를 통해서 볼 수 있으나,성능은 시스템의 구성에 있어서 하나의 Factor일뿐 시스템의 Fail Over나, Clustering으로 인한 네트워크 부하, 관리상의 문제, 비용등 여러가지 요소를 판단해서 HW의 구성을 결정하는것이 필요하다.

이 수치는 어디까지나 MedRec Application에 국한된 수치이며 일반화된 수치는 아니다. Clustering상에서 Session 복제나 JNDI복제등의 부하가 많다면 반대로 수평적 확장이 수직적 확장에 비해서 성능향상에 기여하지 못할 수 도 있다. 즉 하드웨어의 구성은 Application의 성격에 따른 테스트를 통해서 어떤 구성이 좀더 나은 구성인지를 테스트한후에 결정해야할 문제이다.



<그림 7. 웹로직의 수직적 확장>







WAS Instance 수의 산정


웹로직의 구성을 설계하다보면 가장 문제가 되는 부분중에 하나가 몇개의 웹로직 인스턴스를 띄울것인가? 이것이 숙제가 된다.

하나의 BOX에 하나의 웹로직 인스턴스를 띄우는것과 여러개의 인스턴스를 띄우는것에는 일반적인 경우에는 성능상에 큰 차이는 나지 않는다. (하나의 인스턴스가 더 성능은 좋다.)
OS에서 각 Process에 CPU 자원을 Scheduling정책에 따라서 지정하기 때문이다.

그러나, 몇몇가지 요인이 웹로직을 여러개의 인스턴스로 나누어서 기동하도록 하고 있다.

웹로직 BOX당 인스턴스의 수를 결정하는 주요 FACTOR는 다음과 같다.


  • 메모리 크기
  • 전체 쓰레드수
  • GC PAUSE시간과 횟수
  • FAIL OVER
  • CPU수

    메모리 크기
    웹로직 인스턴스당 HEAP SIZE는 1GB이내가 적당하다.
    JVM에 따라서 대용량 메모리를 사용할경우에는 별도의 설정이 필요한 경우가 있고, 1GB가 넘는 대용량 메모리의 경우에는 FULL GC 시간이 많이 걸려서 시스템의 PAUSE TIME이 길게 나와서, 시스템을 정지시킨것처럼 보일 수 있기 때문에 1GB 단위로 웹로직 인스턴스를 나누는것이 좋다.

    전체 쓰레드수
    웹로직에서는 쓰레드를 Pooling을 이용해서 사용하고 있기 때문에, 쓰레드 수를 늘린다는것은 쓰레드를 위해서 메모리등의 자원이 그만큼 많이 할당된다는것을 의미한다. 또한 JVM에서 OS쓰레드와 JAVA의 쓰레드를 맵핑하는 방법에 따라서 그 성능에 차이가 나게 된다.
    일반적으로 한 인스턴스당 최대의 성능을 낼 수 있는 쓰레드수는 50~100개 사이가 된다.

    목표로 하는 Active user 수 (목표 Thread수)가 100개 이상인경우, 하나의 웹로직 인스턴스를 100개의 쓰레드 단위로 나누어서 기동하는것이 좋다.

    예를 들어, 목표 Active User가 300인경우에는 300개의 쓰레드를 가지고 있는 하나의 웹로직 인스턴스가 아니라 100개의 쓰레드를 가지고 있는 3개의 웹로직 인스턴스 또는60개의 쓰레드를 가지고 있는 5개의 웹로직 인스턴스를 기동하는것이 적절하다.
    (※ 100개짜리 3개나, 60깨짜리 5개냐는 각 인스턴스의 성능을 테스트를 통해서 측정한 내용과, 관리상의 편의성,메모리양, GC상황등을 고려해서 기동해야 한다.)

    GC PAUSE 시간과 횟수
    JVM의 Full GC의 경우 Java Application을 순간적으로 멈추게 하기 때문에 Full GC에 소요되는 시간과 횟수는 WAS운용에 있어서 큰 변수로 작용한다.
    Full GC는 웹시스템을 기준으로 하루 5회 미만, 1회당 3~4초 정도가 적정한 수치이다.
    인스턴스 수를 늘리면 각각의 인스턴스에서 일어나는 Full GC의 횟수는 당연히 줄어들게 된다.
    Full GC 내용 역시 인스턴스 수를 결정하는 요인이 된다.

    Fail Over
    웹로직 인스턴스를 나누는 주요한 요인중의 하나가 웹로직 인스턴스에 대한 장애시에 Fail Over이다. 전체 시스템을 하나의 인스턴스로 구성하는것보다 최소 2개 이상의 instance로 구성해서 장애에 대비할 필요가 있다.

    CPU수
    보통 하나의 웹로직 인스턴스를 기동하는데는 1~2개 정도의 CPU를 사용하는게 최적화된 환경이다. 즉 8CPU 머신의 경우 4~8개의 웹로직 인스턴스가 적정하다.
    CPU수보다 많은 웹로직 인스턴스를 기동할경우에는 각각의 웹로직 인스턴스에 CPU가 배정 되는 시간이 느려지기 때문에 성능 저하로 이어질 가능성이 높다.






    결론

    정리를 해보자면 각각의 웹로직 인스턴스를 나누는 기준은 다음과 같다.
    하나의 도메인에 대해서 2개 이상의 인스턴스를 사용해서 장애에 대처해야 하며, 하나의 인스턴스는 1GB이하의 Heap을 가지며, 50~100개 사이의 Thread를 가진다. Full GC 는 하루에 5회 미만으로 일어나며, Full GC 시간은 3~4초 이내가 된다.



  • [바로가기 링크] : http://coolx.net/cboard/develop/682


    Name
    Password
    Comment
    Copyright © 1999-2017, swindler. All rights reserved. 367,611 visitor ( 1999.1.8-2004.5.26 ), 2,405,771 ( -2017.01.31)

      2HLAB   2HLAB_Blog   RedToolBox   Omil   Omil_Blog