본문 바로가기
Programming

Artillery 부하테스트 사용 방법 [Node.js test]

by 개발자 염상진 2022. 9. 18.

백엔드 작업을 할 때 가장 신경써야 하는 부분은 뭘까요? 

Restful API를 작성한다고 할 때 DB Schema도 작성해야 하고, router, controller도 작성해야 한다. 하지만 이 이상으로 중요한게 서버가 얼마만큼의 요청을 견딜 수 있냐 하는 것입니다. 

서버를 제작할 때 엄청난 양의 트래픽을 감당하기 위해서 서버를 이중 삼중으로 두거나, DB 손실을 막기 위해 클러스터링 + 레플리카 셋 구성 + 샤딩 처리 등으로 데이터 손실을 막고 사용자 요청에 유연하게 대응할 수 있는 방법을 마련합니다. 

 

 

 

 

서버 제작을 할 때 유닛테스트를 진행하고, 제작이 완료되었다면 부하 테스트 혹은 스트레스 테스트(Stress Test)를 진행해봐야 합니다. 이 테스트를 통해서 서버의 성능을 가늠할 수 있고, 불필요하게 DB접근하는 로직을 수정해서 성능을 개선할 수 있고, 차후 성능 개선을 위해 어떤 방향이 필요한지 생각해볼 수 있습니다.

 

 

Artillery 부하 테스트 방법

 

① Install

Artillery 설치는 진행중인 프로젝트에서 Local로 진행하거나 전역으로 설치할 수 있습니다.

Local 설치

$ npm install -D artillery@latest

 

전역 설치

$ npm install -g artillery@latest

 

설치가 완료 되면 정상적으로 설치되었는지 확인 할 수 있습니다. 아래 공룡이 등장하면 정상적으로 설치가 되었습니다. 

$ artillery dino
 ------------
< Artillery! >
 ------------
          \
           \
            __ 
           / _) 
    .-^^^-/ / 
 __/       / 
<__.|_|-|_|

 

 

 

 

 

② CLI 테스트

Atillery를 사용하기 위해서는 JSON 혹은 YAML 파일을 생성해서 시나리오를 작성하는 형식으로 부하 테스트를 진행할 수 있습니다. 하지만 CLI에서 quick 명령어로 특정 URL로 바로 테스트해볼 수 있습니다. quick 명령어를 사용하는 경우 /GET 요청만 가능하므로 POST / PUT / DELETE HTTP 함수를 사용하기 위해서는 테스트 코드를 작성해줘야 합니다.

$ artillery quick --count 20 --num 100 https://localhost:9001/test

 

  • --count : 20명의 가상 유저를 생성하여 요청합니다.
  • --num : 유저당 100번의 요청을 생성해서 요청을 보냅니다. 

 

③ Test Script 작성

Artillery 공식 홈페이지에서는 Test Script를 YAML 파일로 작성하고 있지만, JSON이 더 익숙하신 분들은 JSON으로 작성하셔도 무방합니다. Test Script는 크게 2개의 꼭지점으로 구성됩니다.

  • config : 부하테스트를 진행할 환경을 설정합니다.
  • scenarios : 부하테스트를 진행할 순서를 지정합니다. 

 

config 부분에는 테스트를 진행할 target URL과 초당 몇번의 요청을 던질지 결정합니다.

"config": {
        "target": "http://localhost:9001",
        "phases": [
            {
                "duration": 60,
                "arrivalRate": 3,
                "name": "Main Test"
            }
        ]
    },
  • target : 테스트를 진행할 URL
  • phases : 초당 몇번의 요청을 보낼지 결정
    • duration : 몇초간 테스트를 진행할 건가
    • arrivalRate : 몇명의 가상 유저를 설정할 건가
    • name : 테스트 이름 지정

 

Artillery는 실제 서버를 사용하는 User의 입장이 되어 시나리오를 작성할 수 있습니다.

예를 들어 사용자가 최초 메인 페이지에 접속한 후 

  1. 먼저 로그인을 하고
  2. 채팅을 하고자 하는 사용자를 검색하고
  3. 선택한 사용자와 채팅방을 열어 대화를 나눈다

라는 가상의 시나리오를 작성해서 해당 유저가 정해진 시간안에 N명의 가상 유저를 설정해서 테스트해볼 수 있습니다. 서비스를 만드는 개발자라면 DAU가 몇만명은 되는 걸 꿈꾸지만 동시에 그만큼의 유저를 감당할 수 있을만한 서비스를 준비해야 하는 것도 사실입니다.

시나리오 테스트 스크립트는 아래와 같이 작성합니다.

 

 

 

 

"scenarios": [
        {
            "name": "Scenario",
            "flow": [
                {
                    "post": {
                        "url": "/api/test1",
                        "json": {
                            "email": "email@gmail.com",
                            "password": "1111"
                        },
                        "capture" : {
                            "json" : "$._id",
                            "as" : "userId"
                        }
                    }
                },
                {
                    "think": 5
                },
                {
                    "post": {
                        "url": "/api/test2",
                        "json": {
                            "password": "1111",
                            "testValue": "testValue"
                        },
                        "capture" : {
                            "json" : "$._id",
                            "as" : "dataValue"
                        }
                    }
                },{
                    "think": 10
                },
                {
                    "post": {
                        "url": "/test3/{{ userId }}",
                        "json": {
                            "password": "1111",
                            "data": "{{ dataValue }}",
                        }
                    }
                }
            ]
        }
    ]

 

  • name : Scenario의 이름을 명시합니다. 
  • flow : 가상의 유저의 입장에서 시나리오를 작성합니다.

 

응답 데이터 활용 방법

flow 는 배열로 잡혀있으며, 배열 안에는 객체 형식으로 각 요청을 구분해서 지정할 수 있습니다.

요청 body에 담기는 데이터는 json 키값에 담아서 보낼 수 있습니다.

응답에 담기는 body 데이터는 captrue을 사용해서 잡을 수 있습니다. 예를들어 "/api/test2"에 POST 요청 응답 데이터가 아래와 같다고 가정합니다.

_id값을 받아오기 위해서는 $._id로 데이터를 받을 수 있습니다. 즉 $이 응답데이터라고 생각하시면 편합니다. 이제 as 키워드를 사용해서 받아온 데이터에 이름을 지정할 수 있습니다. 

// POST /api/test2 

data : {
    _id : "asdjf023ijkflsjsk",
    name : "About Tech",
}

 

이름을 지정한 데이터를 사용하기 위해서는 넌적스 문법을 사용합니다. {{ 변수 }} 형식으로 사용하시면 params나 query로도 사용할 수 있고, Body데이터에 태워서 사용할 수 도 있습니다.

 

부하테스트 딜레이?

Artillery에서 딜레이는 think 키워드를 사용합니다. 실제 사용자가 로그인을 한 후 바로 서비스를 이용하지 않고 2~5초 정도의 딜레이가 있으므로 이를 감안해 flow 요청 시나리오 중간중간에 think 키워드를 넣어주시면 딜레이를 할 수 있습니다. 위 예제에서는 5초, 10초간 딜레이를 주었습니다.

 

 

작성이 완료된 테스트 스크립트를 MainTest.json이름으로 저장하고 부하테스트를 실행합니다.

$ artillery run MainTest.json

 

부하테스트가 완료되면 아래와 같이 리포트가 출력됩니다. 저는 2개의 요청을 60초간 8명의 가상 유저를 선택해서 부하테스트를 돌렸고, 2 * 60 * 8 = 960개의 request가 나왔고, 960개의 응답을 받아왔습니다. 여기서 가상 유저를 조금만 올려도 응답 비율이 현저히 떨어지는 걸 볼 수 있습니다. 

 

 

Summary report @ 21:06:04(+0900)
--------------------------------

http.codes.200: ..................................... 960
http.request_rate: .................................. 14/sec
http.requests: ...................................... 960
http.response_time:
  min: .............................................. 92
  max: .............................................. 744
  median: ........................................... 232.8
  p95: .............................................. 459.5
  p99: .............................................. 561.2
http.responses: ..................................... 960
vusers.completed: ................................... 480
vusers.created: ..................................... 480
vusers.created_by_name.Main Test...  480
vusers.failed: ...................................... 0
vusers.session_length:
  min: .............................................. 5262.9
  max: .............................................. 5937.9
  median: ........................................... 5487.5
  p95: .............................................. 5711.5
  p99: .............................................. 5826.9

 

맨 마지막에 나오는 session_length는 응답하는데 얼마나 걸렸는지 보여주는 지표입니다. 가장 오래걸린 요청이 5초가 걸렸고, 거의 평균에 근접하고 있어 양호한 결과라고 할 수 있습니다. 하지만 이것도 가상 유저를 늘리면 편차가 급격하게 달라지게 됩니다. 

 

Artillery 보고서 출력

 

Artillery를 통해 진행한 부하테스트 결과를 Report 형식으로 받아볼 수 있습니다. 

리포트를 파일로 출력하기 위해서는 -o 명령어를 사용합니다.

$ npx artillery run -o MainTestReport.json MainTest.json

 

 

부하테스트가 종료되면 MainTestReport.json이 생성됩니다. report 명령어를 치면 테스트 결과를 시각적으로 표현한 HTML 파일이 생성됩니다.

$ npx artillery report MainTestReport.json

 

파일을 웹브라우저에서 실행하거나 VC Code 플러그인인 Live Server로 실행하면 리포트를 확인할 수 있습니다.

 

 

부하 테스트 결과를 확인한 후 서버에 어떤 부분을 수정해야 할지 구체적으로 가늠해볼 수 있게 됩니다. 

 

 

 

 

WebSocket VS Socket 차이점 (WS HTTP 차이)

서버와 클라이언트가 통신하기 위해서는 우선 연결이 확립되어야 합니다. 기본적으로 클라이언트는 서버에 정보를 요청하고 서버는 단순히 응답을 하는 형식입니다. 기존의 HTTP 통신은 TCP/IP 소

about-tech.tistory.com

 

 

React render 두번 호출되는 이유? (useEffect는 잘못없음)

리액트로 소셜 앱을 만드는 과정에서 렌더링이 두번씩 되는 현상이 발생합니다. 가장 기초적으로 useEffect()에 종속 배열로 관리하고자 하는 state가 변경되면 렌더링이 계속 발생하지만, 종속배열

about-tech.tistory.com

 

 

Javascript 함수 실행시간 timer 사용법 시간 측정

함수 실행시간을 측정하기 위해서는 UNIX Time을 사용합니다. 우선 함수를 시작하기전 UNIX TIME을 구하고 함수가 종료된 시점의 UNIX TIME을 구해서 시간차를 계산하면 함수를 실행하는데 얼마나 많은

about-tech.tistory.com

 

댓글