Locust
- API 부하 테스트를 위하여 리서치 후 찾게 된 Python 기반의 부하 테스트 라이브러리.
- 다른 Python 코드에서도 통합이 가능.
- pip를 통해 간편한 설치 가능.
- locustfile.py 파일 하나로 구성 가능.
- RPS(Request Per Seconds), RT(Response Time) 제공.
- 싱글 머신에서도 충분한 부하 테스트가 가능하고, 필요하다면 분산 클러스터를 구성할 수 있으며 그 구성이 간단하게 가능.
구성 요소
# locustfile.py
...
# 동작을 실행할 User
# Web UI에서 User 지정 수 만큼 클래스 인스턴스가 생성됨.
# 각 클래스 인스턴스는 선택된 task를 co-routine 생성하여 실행.
# 각 co-routine은 task로 지정된 함수를 실행.
class User(FastHttpUser):
# 클래스 인스턴스가 스레드를 생성하여 함수를 실행한 뒤, 다음 실행까지 대기하는 시간.
wait_time = between(1, 1.5)
# 클래스 인스턴스가 스레드를 생성하여 실행하는 함수.
# @task 데코레이터를 통해 해당 함수가 task인지 지정 가능.
@task(1)
def api1(self):
self.client.get(path)
@task(10)
def api2(self):
self.client.get(path)
- FastHttpUser
- Locust에서 제공하는 기본 HTTP User는 HttpUser 클래스를 사용한다.
- HttpUser 는 기본적으로 python-requests라는 라이브러리를 사용하는데, FastHttpUser는 geventhttpclient를 사용한다.
- python-requests 보다 CPU 사용량이 적어 HttpUser 보다 7~8배 빠르다.
- client
- HttpSession 의 인스턴스이다. 쿠키 지원.
- get(url, name, ...)
- url : 요청을 할 URL
- name : URL이 고정된 값이 아니고 여러 URL이 발생하게 된다면 그 URL들을 하나로 묶는 네임스페이스 역할을 한다.
- 클래스를 여러 개를 생성하게 될 경우, 다양한 유형의 유저가 API를 호출하는 시나리오를 쉽게 구성할 수 있다.
- Locust에서 제공하는 기본 HTTP User는 HttpUser 클래스를 사용한다.
- @task
- @task라는 데코레이터로 지정한 함수만 실행할 task로 지정이 된다.
- 기본적으로 데코레이터로 지정한 task가 여러 개 라면, 인스턴스는 랜덤으로 하나의 task를 실행한다.
- @task(1), @task(10) 이런 식으로 데코레이터에서 값을 파라미터로 사용할 수 있는데, 이는 가중치를 의미하며 만약 위와 같이 두 개의 task가 지정되었다면 총 실행하는 task 개수에서 @task(1)은 9.1% @task(10)은 90.9%의 비율로 실행되게 된다.
실행
- pip를 이용하여 locust 설치 (python 3.7 이상)
pip install locust
- CLI를 통해 실행.
- 기본 port 인 8089로 웹을 통해 모니터링이 가능하다.
locust
동작 방식
- Number of Users
- task를 실행하는 User 클래스 인스턴스의 수
- locust 가 실행하는 thread는 커널 레벨의 thread 가 아닌 light-weighted thread인 co-routine이다.
- 실제로는 하나의 싱글 스레드에서 여러 개의 클래스 인스턴스가 co-routine으로 task를 실행한다.
- task를 실행하는 User 클래스 인스턴스의 수
co-routine vs thread
- 동시성 처리 측면에서 코루틴은 하나의 스레드 내에서 함수를 호출하기 때문에, context-switching 비용과 메모리를 절약할 수 있는 장점이 있다.
- gevent : co-routine 기반의 python networking library
- python의 generator와 asyncio를 활용함.
- https://www.gevent.org/
- Spawn rate
- 초당 생성 할 유저의 수
- Host
- 부하 테스트를 실시할 Host의 주소
- Run time
- 테스트를 어느 시간만큼 실행할 것인지에 대한 설정
결과 분석
- 어느 시점에서 User가 늘어나도 RPS가 늘지 않는 이유
- task가 서버에 get 요청을 할 때, response를 받지 않으면 task가 끝났다고 판단하지 않기 때문에 다시 task를 실행할 때까지 RT + wait_time 만큼 blocking이 된다.
- RPS는 '초당 Request 수' 이기 때문에 response를 받지 못한 인스턴스들은 request를 날리지 못한다.
- RPS는 유지되고 RT가 증가하면 API 서버 쪽에서 병목이 발생하는 것이 아닌가 의심을 해야 한다.
- User가 늘어나도 RPS는 유지되고 RT도 증가하지 않는다면 부하 테스트를 실시하는 테스트 서버 측의 리소스 한계를 의심해야 한다.
- Master-Worker 구성의 분산 클러스터 구축이 필요할 수 있음.
'Backend' 카테고리의 다른 글
FastAPI로 Serving API 구축하기 (0) | 2023.05.27 |
---|---|
EFK 스택을 통한 Airflow 로그 모니터링 구축 (0) | 2023.03.17 |
Fluentd 기본 개념 (1) | 2023.03.14 |