이게 2022년 마지막 글이 될 것 같네요..

IT 아카데미에서 JMeter에 대해 강의를 했지만 생각보다 교육생들이 사용법을 많이 어려워 하는 것 같아 도구를 바꾸기로 했습니다. 그래서 선택한게 Locust입니다. 개발자 레벨에서는 간단하게 사용하기가 쉽고, 2022년 부하테스트 도구 Best5에도 들었기 때문입니다.

공식 홈페이지의 랜딩이나 도큐먼트에 나와있는 코드를 따라하면 그냥 단일 부하만 주고 종료도 Ctrl-C로 종료시키는 방법으로 종료를 해야 합니다. JMeter처럼 부하를 늘렸다 줄였다 하면서 시간되면 끝나가 하는 뭔가가 있을 거 같습니다.

공식 문서에 있는 “Custom Load Shapes”를 참조하여 만든 아래 코드를 보겠습니다.

from locust import HttpUser, TaskSet, task, constant
from locust import LoadTestShape


class UserTasks(TaskSet):
    @task
    def get_root(self):
        self.client.get("/")


class WebsiteUser(HttpUser):
    wait_time = constant(0.5)
    tasks = [UserTasks]





class StagesShapeWithCustomUsers(LoadTestShape):
    """
    A simply load test shape class that has different user and spawn_rate at
    different stages.
    Keyword arguments:
        stages -- A list of dicts, each representing a stage with the following keys:
            duration -- When this many seconds pass the test is advanced to the next stage
            users -- Total user count
            spawn_rate -- Number of users to start/stop per second
            stop -- A boolean that can stop that test at a specific stage
        stop_at_end -- Can be set to stop once all stages have run.
    """

    stages = [
        {"duration": 6, "users": 10, "spawn_rate": 10, },
        {"duration": 10, "users": 50, "spawn_rate": 50, },
        {"duration": 18, "users": 100, "spawn_rate": 100},
        {"duration": 22, "users": 30, "spawn_rate": 30},
        {"duration": 23, "users": 10, "spawn_rate": 10},
        {"duration": 30, "users": 1, "spawn_rate": 1},
        
        {"duration": 36, "users": 10, "spawn_rate": 10, },
        {"duration": 40, "users": 50, "spawn_rate": 50, },
        {"duration": 48, "users": 100, "spawn_rate": 100},
        {"duration": 52, "users": 30, "spawn_rate": 30},
        {"duration": 53, "users": 10, "spawn_rate": 10},
        {"duration": 60, "users": 1, "spawn_rate": 1},
    ]

    def tick(self):
        run_time = self.get_run_time()

        for stage in self.stages:
            if run_time < stage["duration"]:
                # Not the smartest solution, TODO: find something better
                try:
                    tick_data = (stage["users"], stage["spawn_rate"])
                except KeyError:
                    tick_data = (stage["users"], stage["spawn_rate"])
                return tick_data

        return None

위 코드에서 보면 LoadTestShape를 상속받고 여기에서 tick()함수를 사용해 duration, users, spawn_rate 를 컨트롤 할 수 있습니다.

여기에서 부하 데이터는 stages 입니다. stages의 데이터 구조에서 duration은 부하 테스트의 진행 시간, users는 사용자수, spawn_rate는 사용자 추가 속도를 의미합니다. 만일 spawn_rate가 1이면 1user/sec입니다. 10이면 10user/sec가 되겠죠.

{"duration": 6, "users": 10, "spawn_rate": 10, }의 의미는 처음부터 6초가 지날 때 까지 사용자 10명을 10user/sec의 속도로 추가한다는 얘기입니다. stages는 사용자를 10 -> 50 -> 100 -> 30 -> 10 -> 1 로 바꾸면서 2회 테스트를 진행하는 시나리오 입니다.

이를 수행해 보면 그래프는 다음과 같습니다.

기록을 위해 여기에 남깁니다.