티스토리 뷰
부하 테스트의 목적
최근 사용자가 많이 증가함에 따라, 저희 서비스의 주요 기능 중 하나인 ‘챗봇 서비스’가 사용되었을 때 DB 사용량의 증가로 인해서 서버 응답 시간이 수직적으로 상승하는 통계를 확인한 바 있었습니다. 그러다가 결국 8월 말, 응답 대기시간을 초과해서 발생하는 ‘타임아웃’이 폭발적으로 일어나는 것이 30분 동안 지속하였습니다.
따라서 해당 API가 단위 시간당 어느 정도의 트래픽을 버틸 수 있는지 성능을 측정하고, 개선점을 찾기 위해서 이번 부하 테스트를 계획하게 되었습니다.부하 테스트란?
서버 개발을 하면서 저희는 지금까지 1명의 사용자에 대한 기능 테스트만 진행했습니다.
기능 테스트란, 말 그대로 우리가 코딩한 기능이 `제대로 작동하는가?`에 대한 질문을 해결하는 하나의 방법이며, 반면에 부하 테스트는 기능 검증은 물론, `사람들이 한 번에 많이 몰렸을 때, 안정적인가?` 그리고 `현재 서버의 스펙으로 어느 정도까지 수용할 수 있는가?`에 대한 질문을 해결하는 방법입니다.
즉, 다시 말해서 부하 테스트는 여러 명이 동시에 진행하는 기능 테스트이죠!
무엇이 좋아요?
결론적으로, ‘많은 사용자가 기능을 수행하는 부분’을 대비할 수 있습니다.
기능 테스트는 ‘기능’이 의도한 대로 잘 작동하는지만 판단하기 때문에, 일반적으로 다량의 동시접속이나 성능을 고려하지 않습니다.
따라서 기능 테스트를 했을 때, 응답 시간이 빨라서 프로덕션 환경에 배포하였지만, 해당 환경에서 가령 1,000명이 동시접속 했을 때, 응답이 지연되어서 결국 서버가 다운되는 문제점이 발생할 수 있습니다.
분명 배포하기 전에 ‘테스트’를 했지만, 배포 후 프로덕션 환경에서는 서버가 다운되는 문제가 발생합니다.
이러한 상황을 미리 대비하는 데 필요한 부분에 대해서 부하 테스트를 진행하는데요, 1분에 100명, 1,000명, 5,000명 등 늘려가면서 작성한 서버가 문제없이 잘 작동하는지 체크합니다.
기능 테스트에서 문제가 없었지만, 부하 테스트에서 많은 지연시간이 발생하는 경우 실제 프로덕션 환경에 적용하기 전에 빠르게 수정해서 안전하게 적용할 수 있는 것이죠!
JMeter
부하 테스트를 사용할 수 있는 방법은 직접 병렬 요청을 보내는 코드를 작성해 테스트하는 방법과 기타 자동화 툴을 사용하는 테스트 방법이 있으나, 이번 글에서는 ‘JMeter’라는 툴로 저희가 직면했던 문제점을 ‘발견’하는 과정을 소개하려고 합니다.
JMeter은 Apache 에서 제공하는 오픈소스 부하 테스팅 툴 입니다. HTTP / FTP / MoM[Message Oriented Middleware] 등 여러 가지 프로토콜을 지원하고, 스레드를 사용해 여러 사용자가 동시에 접속하는 것처럼 테스트할 수 있습니다. 대표적으로 ‘스레드 그룹’과 ‘HTTP 요청’을 이용해서 부하 테스트를 간단히 클릭 몇 번으로 할 수 있죠.
첫 JMeter 화면
새로운 테스트를 만들려고 하면 나오는 화면입니다.
여기서부터 정말 많은 설정을 할 수 있는데요, 이번 글에서는 ‘채팅 메시지’를 가져오는 요청을 대량으로 보낼 것이기 때문에, 간단한 설정과 후처리만 진행될 예정입니다.
스레드 그룹 추가
스레드 그룹 안에 들어가는 모든 작업과 순서들은 ‘하나의 스레드’ 안에서 진행됩니다. 즉, 스레드 그룹 안에 들어있는 모든 작업 정의들을 여러 스레드에서 동시에 실행되는 것이죠.
대표적인 Thread Property
저희가 주로 사용했던 설정값들에 대한 설명입니다.
- Number of Threads(users)
⇒ 몇 명이 동시접속 할 것인가?
[즉, 몇 명이 Thread Group 내에 정의된 일들을 수행할 것인지?] - Ramp-Up Period(seconds)
⇒ 전체 스레드가 몇 초 안에 실행을 마쳐야 하는가
[보장 X → 빠르게 끝날 수도, 늦게 끝날 수도 있음] - Loop Count
⇒ 스레드 그룹에 있는 내용들을 몇 번 반복해야 하는가 - Same User on Each Iteration
⇒ 반복 시, 같은 사용자[스레드]를 사용하도록 검사하는 부분
즉, “Number of Threads”가 동시 접속할 ‘사람 수’에 해당하고, Ramp-Up Period로 ‘몇 초 안에 몇 명’을 설정할 수 있는 것입니다.
이번 글에서는 설명을 위해 분당 2000번 정도로 설정하였고, 실제 테스팅 환경에서는 분당 5000 - 7000회 정도로 두고 테스트를 진행하였습니다.
사용자 정의 변수 설정
‘사용자 정의 변수’란, 요청 전반에 있어서 쓸 수 있는 하나의 ‘전역변수’와 비슷한 개념입니다. 여기에 적용된 변수는 어떠한 요청에서도 가져다 쓸 수 있고, Post Body나 URL Parameter 등에서 광범위하게 쓰일 수 있습니다. 저희 경우에는 서버의 주소와 서버 포트, 그리고 GET 요청을 보낼 때 필요한 특정 ID 들을 미리 정의해 놓았습니다.
후술할 BeanShell PreProcessor에서 ‘사용자 정의 변수’를 불러와서 파싱도 적절히 할 수 있어, ID 같은 경우에는 차례대로 가져와서 요청을 보낼 수 있게 쉼표로 구분하여 여러 개 넣었습니다.
BeanShell Pre-Processor
BeanShell Pre-Processor는 Java 언어로 작성할 수 있는, Pre-Processor입니다.
Java와 같지만, 추가로 Beanshell Pre-Processor에서는 요청 스레드 번호, 사용자 정의 변수 리스트 등을 JMeter 요청에 필요한 다양한 변수 및 클래스를 제공하고 있습니다.(BeanShell Pre-Processor에 대한 자세한 내용은 여기를 참고해 주세요!)
저희 케이스에서는 다음과 같은 내용을 진행했습니다.
- 사용자 정의 변수에 선언된 여러 개의 ID 들(쉼표로 구분)을 배열화
- 현재 스레드 번호에 해당하는 ID를 가져오기[인덱스 접근 — 스레드 번호는 0번부터 시작]
- 선택된 번호를 ‘idSelected’라는 사용자 변수에 새로 넣어줌
저희는 사용자 정의 변수로 여러 개의 ID를 넣어 두었는데, 이를 각 요청[스레드]에서 하나씩 쓸 수 있도록 구성하기 위해서 해당 Pre-Processor를 사용했습니다. 추가로 선택된 ID를 idSelected라는 새로운 변수로 넣어주어서 HTTP 요청에서 `idSelected`를 호출해 선택된 ID를 가져올 수 있게 합니다.
Request
이전까지의 단계들이 모두 ‘준비’였다면 해당 단계에서는 ‘실행’ 단계입니다.
HTTP 요청을 보낼 수 있으며,
- Protocol
- Host[Server Name]
- Port Number
- HTTP Method
등을 자유롭게 설정할 수 있습니다.
저희는 HTTP API를 테스트하니, 프로토콜은 HTTPS로 하고, 서버 주소와 포트, 그리고 BeanShell PreProcessor에서 가져온 ID값은 이전에 설정했던 ‘사용자 정의 변수’에서 불러와서 실행되게 설정하였습니다.(‘사용자 정의 변수’에서 불러오는 방법은 단순히${변수이름}입니다.)
Response
사실 이 이후에 바로 요청을 걸 수 있지만, 결과에 대해서 알 수 없으니 결과를 보기 위해서 ‘View Results Tree’를 생성합니다! 아래 사진에 나온 화면과 같으며, 결과를 보기 위해서 따로 설정할 것은 없습니다.
실행 및 확인
상단에 있는 ‘플레이’ 버튼을 눌러서 테스트를 진행해 보고 결과를 확인해 보세요!
저처럼 제 시간 안에 요청이 모두 정상적으로 들어왔는지, 혹시 실패한 요청은 없는지 살펴봅니다.
Result
위 결과에서 볼 수 있듯이 초록색 보다, 빨간색이 주를 차지하는 것을 볼 수 있습니다.(…)
분당 3,000회 정도의 요청 — 즉 초당 50회 정도의 요청 — 값을 단순히 조회하는 요청인데도 불구하고 서버가 감당하지 못했습니다. 마지막 1분 30초 중, 30초는 모두 빨간색으로 도배된 것을 실시간으로 확인했었습니다.
저희는 해당 API에, ‘JMeter을 통해서 많은 사용자가 동시에 접근해서 요청을 보낼 때, 서버가 감당할 수 있는가?’ 라는 질문을 던졌지만, 결국 돌아오는 대답은 부정적인 것으로 확인되었습니다.
'Server' 카테고리의 다른 글
[Java] JPA 사용시 Truncate 사용방법 (4) | 2021.11.25 |
---|---|
[Java] DTO에 자동 호출 로직 적용하기 (2) | 2021.11.22 |
[Java] QueryDSL 을 사용하여 Multi Data Source 사용하기 (0) | 2021.11.13 |
Mysql Timestamp 과 Datetime의 차이 (0) | 2021.11.12 |
[Java] QueryDSL 에 대해 알아보자 (0) | 2021.11.10 |
Nginx 설정을 웹에서 테스트하고 공유할 수 있는 곳 (0) | 2021.11.10 |