ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Jmeter 사용기
    JAVA공부/JAVA 2023. 3. 6. 01:49

    Jmeter란

    아파치 프로젝트를 통해 제작된 Java응용 프로그램 부하 테스트, 성능측정을 하기 위한 오픈 소스 프로그램이다.   

    초기에는 웹응용 프로그램을 테스트하기 위해 제작되었으나, 추후 다른 테스트 기능도 제공하게 되었다.

     

    Jmeter가 제공하는 테스트들

    • 웹 - HTTP, HTTPS (자바, 노드 JS, PHP, ASP.NET, ...)
    • SOAP / REST 웹 서비스
    • JDBC를 통한 데이터베이스
    • FTP, TCP, LDAP
    • JMS를 통한 메시지 지향 미들웨어(MOM)
    • 메일 - SMTP(S), POP3(S) 및 IMAP(S)
    • 기본 명령 또는 셸 스크립트
    • 자바 객체

    설치하기 

    Apache JMeter - Download Apache JMeter

     

    Apache JMeter - Download Apache JMeter

    Download Apache JMeter We recommend you use a mirror to download our release builds, but you must verify the integrity of the downloaded files using signatures downloaded from our main distribution directories. Recent releases (48 hours) may not yet be ava

    jmeter.apache.org

     

    윈도우는 위의 페이지에서

    를 받도록 한다. (Java 8버전 이상이 설치되어있어야한다.)

     

    Mac의 경우

    homebrew jmeter install

    명령어를 통해 설치를 하도록 하자. (이 후 console에 jmeter를 치면 실행된다.)

     

    압축 해제후 apache-jmeter-5.5\bin 폴더에 있는 ApacheJMeter.jar를 실행한다.

     

     

    실행하면 로그뷰어가 꺼져있어서, 켜두고 시작하는게 좋다.

     

    * Log Level에서 로그 수준을 Debug, Trace등으로 변경할 수도 있다.

     


    시작하기

    1. 쓰레드 그룹 

     

    • Open Modal Thread Group (아직 작업을 해보지는 못함)
      Open Model Thread Group in JMeter - QAInsights
      에 따르면
      일반 쓰레드 그룹은 동시다발적으로 일어나나, 발생을 패턴화하여 작업을 진행시킬 수 있는 그룹이라고 한다. 

    • setUp Thread Group
      Thread Group 작업 전 세팅을 위한 쓰레드 그룹. 
      DB에 접근하여 데이터를 추출하거나, 다른 API를 통해 데이터를 추출

    • Thread Group
      실제 작업을 진행하는 쓰레드 그룹, setUp을 통해 받아온 데이터로 작업을 진행하거나,
      해당 그룹에서 API, JDBC 접근을 통해 작업을 진행 할 수 있다.

    • tearDown Thread Group
      모든 쓰레드 그룹 진행 후 rollback 작업을 하거나 처리해야될 작업을 진행하는 쓰레드 그룹

    참고로 Name을 변경하면 커스텀하게 이름을 설정할 수 있다.

     


     

    2. Property 세팅하기

     Property란 Jmeter 전체에서 적용되는 환경변수를 말한다.
     저장하는 방법은 여러가지이지만(Plugin을 추가로 설치해서 yaml 파일도 가능 -> 외부파일을 또 생성해야해서...), 
    가장 간단한 JSR223 을 사용한 저장방법을 소개하려한다.
    setUp Thread 그룹에서 JSR223 Sampler를 선택한다.

     

     

     

    아래 스크립트 부분에 다음과 같이 저장한다.

     

    ArrayList list = []  //리스트 초기화 선언
    list.add(1) // 1을 넣는다.
    log.info("list = {}", list) //list 정보 출력
    props.put("testList", list) // Property 'testList'에 list를 담는다.
    log.info("testList = {}", props.get("testList")) // Property 'testList' 정보 출력

     

     

     

    Groovy Script라서 기존 Java와 섞인부분이 있지만. 위의 주석을 달았으니 해당부분을 이해할 수 있을 거라 생각한다.

     

    ***여기서 property를 setUp Thread그룹 Sampler로 넣는 이유가 있다.***

    setUp에 넣지않고 테스트 계획 바로 아래에 해당 JSR223 Pre Processors을 넣는다면
    매 쓰레드 그룹마다 Property가 초기화 되어 들어간다. 

     

     


     

    3. HTTP 세팅하기 

    Http 통신을 주고받을때  가장 먼저 설정해야하는 것이 도메인 주소와 Header정보이다.

     

     

     

     

    이 기본값 설정은 테스트계획 바로 아래에 존재해도되며, 상황에따라서 쓰레드 그룹 안으로 넣어도 괜찮다.

    필자는 Request Default를 다음과 같이 설정하였다.

     

     

     

    그 다음은 Header설정이다.

     

     

    Content-Type이(ResponsBody가) 만약 Json인 경우 꼭 명시를 해주어야 오류가 나지 않으며

    Authorization의 경우 대부분 많이 사용하는 Bearer Token값을 예시로 들었다.

    주의해서 보아야할 것은 '${__P(token)}' 이 부분이다.

    이 코드는 Jmeter가 제공하는 Function인데 , 해당부분은 Jmeter에 Property로 등록된 'token'을 가져올때 사용하는

    함수이다.

    Apache JMeter - User's Manual: Functions and Variables

     

    Apache JMeter - User's Manual: Functions and Variables

    < Prev Index Next > 20. Functions and Variables¶ JMeter functions are special values that can populate fields of any Sampler or other element in a test tree. A function call looks like this: ${__functionName(var1,var2,var3)} Where "__functionName" matches

    jmeter.apache.org

    API 통신을 할 때 Token정보가 있어야 통신이 가능한데, 로그인 API 호출 후 Header로 전송된 Token을 Property로 
    저장하고, API통신 시에 함수를 사용하여 값을 매핑시켜주는 코드를 짜놓은 것이다.

     


     

    4. API 호출 후 값 저장하기

    API 호출 후에는 값저장이 필요하게 되는 경우가 많다.
    여기서는 Response Header를 Regular Expression Extractor로 추출하는 방법과

    Response Body (Json)을 Json Extractor로 추출하는 방법을 소개한다.

     

     

    - Response Header - Regular Expression Extractor 

     

    다음과 같이 로그인 API를 요청을 보내보자, 결과를 확인하기 위해서는 다음과같이 Listener에서
    View Results Tree를 배치해야한다.

     

     

     

    요청 후 View Results Tree를 확인해보면

     

    다음과 같이 온다. (보안을 위해서 대부분 정보를지웠습니다.)

    저기서 우리가 뽑아내고싶은 정보는 JSESSIONID인데,

     

     

    로그인 API -> 우클릭 -> Post Processors -> Regular Expression Extractor 후 다음과 같이 입력한다.

     

     

    그러면 어떻게 되느냐? JSESSIONID라는 변수명으로 Variables에 저장된다.

    이렇게 했을 때 주의 점이 있다!

    Variables에 저장되면 해당 쓰레드 그룹에서 벗어날시 해당 변수를 불러오지 못한다.

    따라서 이 값을 Property (글로벌 변수도 가능)에 저장하여 다른 곳에서도 사용 할 수 있도록 한다.

     

     

    JSR223 Post Processors를 추가 등록하여 지역변수 -> 환경변수로 등록하자

    props.put('JSESSIONID', vars.get('JSESSIONID')) //지역변수 JSESSIONID를 전역 Property에 저장
    
    // 주의 vars.get()은 String반환, vars.getObject()는 Object반환

    이렇게 저장하는 경우 다른 곳에서 ${__P(JSESSIONID)} 로 사용하거나

    props.get("JSESSIONID") 로 해당 값을 호출 할 수 있다.

     

    - Response Body - Json Extractor

     

    API를 통해 다음과 같은 결과를 받아와서 변수 저장을 하고 싶은 경우

     

    다음과 같이 결과가 들어오게 되는데

     

    미리 추출을 해보자면 위한 정규식을 보자면 다음과 같이 표현할 수 있다.

     

    BlazeMeter라는 사이트에서 보여주는 샘플 Expression  들로 여러가지 다룰 수 있다.

    Json Path Extractor (blazemeter.com)

     

    JMeter's JSON Path Extractor Plugin - Advanced Usage Scenarios | Blazemeter by Perforce

    The JSON Path Extractor is a JMeter plugin that enables extracting values from JSON responses.

    www.blazemeter.com

     

     

     

    해당 Json Value OR Json Array를 저장하려하면

    이것을 해당 Http Request에서 우클릭 후

    을 눌러서 처리하자

    나는 모든 Json 내용을 orderList로 저장하였다.

     

    이후 이 변수를 사용하려면 (JSR 223에서 가공)

    import groovy.json.JsonSlurper
    
    log.info("orderList class name = {}", vars.getObject("orderList").class.getName())
    String orderListJson = vars.getObject("orderList")
    
    def jsonSlurper = new JsonSlurper()
    ArrayList orderList = jsonSlurper.parseText(orderListJson)
    
    def order = orderList.stream()
        .filter(item -> item.productId.equals("CATALOG-001"))
        .findFirst()
        .orElse(null)
    
    log.info("order = {}", order)

    주의해야할 것은  위에서 저장한 변수가 Json String 형태로 저장된다는 것이다. 

    그래서 위와같이 로그를 찍어보면 Class명을 String으로 반환하는 것을 알 수 있다.

    이에 따라서 Jmeter내장 Json -> Object 라이브러리인 JsonSlurper를 이용해서 다시 파싱을 해준다.

    아랫부분은 자바코드와 동일해서 설명을 생략하도록 하겠다.

     


     

     

    5. JDBC 세팅

     

    JDBC세팅 또한 어디서 하나 상관없으나 필자는 테스트계획 밑에 해두었다. (전역설정)

     

     

     

    일반적인 DB설정과 다르지 않다.
    혹시나 발생할 불상사를 방지하기 위해 자동커밋 (Auto Commit) 은 False로 두었다.
    또, DB접근을 위해서 DB Connection Pool 이름을 설정해야하는데,
    DB Request시 해당 이름이 필요하므로 Property로 설정하여 불러올 수 있도록 하였다.

    추가적으로 말해야할 것 이 있는데, 다른 DB는 모르나 MariaDB의 경우 드라이버클래스가 존재하는것처럼
    선택지가 있는데, 해당 드라이버가 존재하지 않아 실행되지 않는다고 떴다. 그래서 Jmeter 라이브러리 폴더에 
    MariaDB Client Connection.JAR를 넣어주었다.

    MariaDB Products & Tools Downloads | MariaDB

     

    Download MariaDB Connectors for data access & streaming | MariaDB

    Download connectors for high-performance data access & data streaming. MariaDB connectors include Python, C, C++, Java 7, Java 8, ODBC, R2DBC and Node.js.

    mariadb.com

     

     

     


     

    6. JDBC 호출

     

    Thread Group 에서 add -> Sampler -> Jdbc Request 를 누르고 다음과같이 입력할 수 있다.

     

    이러면 'th@naver.com' 라는 email을 가진 회원의 이름과 나이를 추출할 수 있는데,

    결과를 항상 List로 저장하기 때문에 데이터를 추출하기 위해 resultSet를 object로 저장하도록 하자.

    해당 List는 members라는 결과변수 이름으로 저장된다.

     

    그 다음 Post Processor에서 데이터를 추출해보자

     

    import java.util.ArrayList;
    
    ArrayList members = vars.getObject('members')
    String name = members.get(0).get("name")
    int point = members.get(0).get("point")
    log.info("name = {}", name)
    log.info("point = {}", point)
    
    vars.put("name", name)
    vars.putObject("point", point)

    밑에 로그로 출력되는 것을 확인 할 수 있다.

     

    **

    만약에 memberList에서 Array 형식으로 members를 뽑아 내려면 다음과 같이 하여도 된다.

    import java.util.ArrayList;
    
    ArrayList members = vars.getObject("members");
    ArrayList seqMemberList = [];
    
    for ( int i = 0; i < members.size(); i++) {
        seqMemberList.add(members.get(i).get('seq_member')); 
    }
    vars.put('seqMemberList', seqMemberList.toString()) //String 으로 variable 저장
    
    log.info("seqMemberList = {}", seqMemberList.toString()) 
    

     

     


     

    7. Csv파일 불러오기

     

    해당 파일을 읽어오자.

    Config Element에서 Csv 데이터 설정이있다.

     

     

     

    주의할것이 있다. 첫번째 빨간줄은 첫번째 열은 무시할거냐 (name, point)와 같은 변수명 => True

    2번째 Delimiter (구분값) 인데 이 때 띄어쓰기가 되어있으면 해당하는 부분도 읽어오니 조심하자.

     


     

    8. 저장한 변수 사용하기

     

    저장한 값들은 다른 설정파일이나 Json Body에서

    // JSR223 지역변수
    vars.getObject("name") // Object
    vars.get("name") // String
    
    // JSON BODY
    {
        "name": ${name} // 혹은 ${__V(name)}
        "point": ${point} // 혹은 ${__V(point)}
    }
    
    
    // JSR223 프로퍼티
    props.get("name") // Object
    
    // JSON BODY 프로퍼티
    {
        "name": ${__P(name)}
        "point": ${__P(point)}
    }

    과 같이 사용할 수 있다.

     

    예시로 request 요청을 하나 만든다면 다음과 같다.

     

     


     

    9. 쉘스크립트(sh) - linux, mac 혹은 배치프로그램(bat) - windows 실행하고 싶을 때

     

    샘플러중 OS ProcessSampler를 생성한다.

     

    나 같은 경우 JVM의 메모리 체크를 하기위해 다음과 같이 하였다.

    원래는 스크립트 한번에 처리할 수도 있지만 공부겸 2개의 OS ProcessSampler를 생성하였다.

    Jstat을 사용하는거는 거의 동일하니 pid가져와서 추출하는 것만 보여드리겠다.

     

    우선 가져올 서버이름을 property로 저장한다.

     

    그다음 Os 샘플러를 생성하는데,

    OS에 따라 command 명령어가 조금 다르다.

     

    Windows 10 인경우

     

     

    Mac인 경우

     

     

     

    이후 가져온 pid는 RegularExpression Extractor로 추출한다. 

     

     

    propery로 저장하거나 로그로 뽑아본다.

     

    **부가적인 설명이 있는데 위에서 정규식이 유효한지 확인하려면 다음과 같은 방법도 가능하다.

     

    우선 우리가 결과를 확인하는  View Results Tree를 보면

     

    저렇게 Text가 있는데, 이걸 클릭하고 

    Extractor를 사용하기전 테스트를 해볼 수 있다.

    클릭 후 

    위에서 Pid값 추출하는 Extractor의 식(Property값을 넣는건 불가능) 을 넣고 Test를 누르면 매칭이 되는 값이 나온다.

     

     


    11. 부가요소

     

    • Jmeter Plugin Manager 사용하기
      Install :: JMeter-Plugins.org
      앞서 소개한 Plugin을 설정하기 위해 해당 Jar파일을 jmeter/lib/ext에 저장하면 
     

    Install :: JMeter-Plugins.org

    Installing Plugins The easiest way to get the plugins is to install Plugins Manager. Then you'll be able to install any other plugins just by clicking a checkbox. If you experience any issues with plugins installation, don't hesitate to ask at Support Foru

    jmeter-plugins.org

    추가적인 기능을 사용할 수 있다.

     

    • Thread Group 끼리 순차적 실행하기 

    다음과 같이 2개의 쓰레드 그룹이 존재할때 순차적으로 실행하고 싶은 경우 (원래는 동시에 실행)

    해당 옵션을 체크하면 순차적인 실행이 가능하다.

     

     

    • 프로퍼티(환경변수) 확인

     

    위에서 변수들을 글로벌 변수로 저장하기 위해 Property로 저장하였는데, 그렇지 않고 테스트 계획 밑에

    add ->  Config Element -> User Defined Variables로 글로벌 변수를 선언해서 사용할 수 있다.

     

     


     

     

    12. 결과치 보기

     

    모든 결과에 대한 결과치를 보려면 테스트 계획에서 다음과 같은 Listener를 가져온다

    이외에도 Generate Summary Results나 Response Time Graph등 다른것에서도 결과치를 볼 수 있으나

    한눈에 볼 수 있는 Summary Report를 우선 확인해보자.

     

    내가 테스트 할 것은 UserId로 상품목록을 가져오는  API이다. 리스트에 총 3개의 오브젝트가 담겨져 있다.

     

    상품 목록 API - PostMan

     

     

     

    테스트하기전 쓰레드 그룹을 먼저 설정하자.

    쓰레드 그룹을 다음과 같은 설정창이 나오는데

     

    Number Of Threads (users): 쓰레드 수 를 의미하는데, 가상으로 동시 접속할 유저의 수 를 정의한다.

    Ramp-up period(seconds): 해당 초 동안 Number Of Threads에서 정의한 숫자만큼의 쓰레드 수를 생성하라는 의미이다.

                                                  만약 Number Of Threads 를 10으로 하고 Ramp-up period를 1로 하면

                                                   1초동안 Thread를 10개 생산한다.

    Loop count: 사용자 1명당 반복할 횟수를 의미한다.

     

    나는 로컬에서 사용했다. (AMD 5800Hs 8C 16T / 16G Ram)

    10 / 0 / 10 로 먼저 가볍게 설정하고 결과창을 보자

    Threads :10 / Ramp-up : 0 / Loop : 10

    Samples =  Thread 수 * Loop 수이다.

    Average = 평균 응답시간 (단위 : millisecond)

    Min = 최소 응답시간 (단위 : millisecond)

    Max = 최대 응답시간 (단위 : millisecond)

    Std Dev = 표준편차

    Error% = 에러율

    Throughput = 시간당 처리량 , TPS(Transaction Per Second)라고 하기도 한다.
                              ( 2083.3/sec => 초당 2083건 처리) ==> 가장 중요함

     

     

    계속 늘려보면서 작업해보았다.

    10 -> 50 -> 1000 -> 5000 -> 10000

    Threads :50 / Ramp-up : 0 / Loop : 10

    5000수 부터 에러가 발생하는 것을 확인했다.  (소켓이 닫혔다는 에러가 나왔다.)

    Threads :5000 / Ramp-up : 0 / Loop : 10

     

    Threads :10000 / Ramp-up : 0 / Loop : 10

    보통 Throughput이 현저히 낮아지거나, 컴퓨터 자원의 80%를 사용할때까지

    쓰레드 수를 늘려보면서 자신의 서버에 동시 사용가능한 수를 추정한다고 한다.

    (웹페이지에 머무는 것이 아닌 동시에 API를 호출하는 유저 수)

     

    참고로 위의 예시는 로그인도 하지 않으며, 비즈니스 로직이 존재하지 않고 단순 조회이기 때문에
    저런 높은 TPS가 나왔다.

     

     

    네이버에서 '테스트' 라는 글자를 검색하면

    (개발자도구에서 확인)

    519 밀리초가 걸리는 것을 확인할 수 있다.


     

    궁금한 것을 Chat Gpt 로 찾아보았다.

     

     

     

Designed by Tistory.