You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Locust负载测试在CI环境中随机失败问题求助

GitHub Action CI中Locust接口负载测试随机失败

本地执行Locust接口负载测试通常能成功,但在GitHub Action CI环境中测试结果不稳定,会随机失败。我通过以下bash脚本在API启动后执行Locust测试文件,期望无需修改参数即可让测试稳定通过,但相同代码在不同CI运行时机下结果不同。

执行测试的Bash脚本

function loadTesting() {
    # check if locust installed
    if! [ -x "$(command -v locust -V)" ]; then
        if! [ -x "$(command -v pip3 -V)" ]; then
            warn_msg "pip3 is not installed. Installing..."
            apt install python3-pip
        fi
        warn_msg "Locust is not installed. Installing..."
        pip3 install locust
    fi
    local HOST=http://127.0.0.1:8080
    local CONCURRENT=500
    local RATE=500
    local DURATION=10
    locust -f ${ROOT_DIR}/load-testing/locust.py \
                         --host ${HOST}          \
                         --headless              \
                         --users=${CONCURRENT}   \
                         --spawn-rate=${RATE}    \
                         --run-time=${DURATION}s \
                         --reset-stats           \
                         --only-summary
}

Locust测试代码

from locust import task, between, FastHttpUser
import string
import random
import time
import datetime

WAIT_TIME_MIN = 0.1
WAIT_TIME_MAX = 0.5

h = {
    "Content-Type": "application/json",
    "Authorization": "Basic YWRtaW46YWRtaW4="
}

random.seed()

class LoadTest(FastHttpUser):
    wait_time = between(WAIT_TIME_MIN, WAIT_TIME_MAX)
    abstract = True
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.base_path = None

    def generate_random_string(self, min_name_size=2, max_name_size=20) -> str:
        letters = string.ascii_lowercase
        string_size = random.randint(min_name_size, max_name_size)
        generated_string = ''.join(random.choice(letters) for i in range(string_size))
        return generated_string

    def generate_random_dob(self) -> str:
        d = random.randint(1, int(time.time()))
        return datetime.date.fromtimestamp(d).strftime('%Y-%m-%d')

    @task(1)
    def get_all(self):
        self.client.get(url=self.base_path, headers=h)

    @task(1)
    def get_one_by_id(self):
        random_id = self.random_user_id()
        self.client.get(url=f"{self.base_path}/by-id/{random_id}", headers=h)

    @task(1)
    def get_one_by_email(self):
        random_email = self.random_user_mail()
        self.client.get(url=f"{self.base_path}/by-email/{random_email}", headers=h)

    @task(20)
    def post_request(self):
        self.client.post(url=self.base_path, json=self._generate_post_data(), headers=h)

    @task(4)
    def put_request(self):
        request_path, request_data = self._generate_put_data()
        self.client.put(url=request_path, json=request_data, headers=h)


class TeacherProcess(LoadTest):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.base_path = "/api/v1/teacher"
        self.teacher_mails = []
        self.counter = 0

    def on_start(self):
        self.client.post(url=self.base_path,
                         json=self._generate_post_data(),
                         headers=h)
    
    def random_user_mail(self) -> str:
        return random.choice(self.teacher_mails)
    
    def random_user_id(self) -> int:
        return random.randint(1, self.counter)

    def on_stop(self):
        for email in self.teacher_mails:
            self.client.delete(url=f"{self.base_path}/by-email/{email}", headers=h)

    def _generate_post_data(self) -> dict:
        request_data = {
            "name" : self.generate_random_string(),
            "email": f"{self.generate_random_string()}@{self.generate_random_string()}.{self.generate_random_string()}",
            "dob": self.generate_random_dob()
        }
        self.teacher_mails.append(request_data["email"])
        self.counter += 1

        return request_data

    def _generate_put_data(self) -> (str | dict):
        request_data = {
            "name": self.generate_random_string(),
            "email": f"{self.generate_random_string()}@{self.generate_random_string()}.{self.generate_random_string()}",
            "dob": self.generate_random_dob()
        }

         # select random email on db
        random_teacher_mail_on_db = random.choice(self.teacher_mails)
        # delete randomly selected email from list
        self.teacher_mails.remove(random_teacher_mail_on_db)
        # add new email to the list
        self.teacher_mails.append(request_data["email"])

        return f"{self.base_path}/{random_teacher_mail_on_db}", request_data


class StudentProcess(LoadTest):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.base_path = "/api/v1/student"
        self.student_mails = []
        self.counter = 0

    def on_start(self):
        self.client.post(url=self.base_path,
                         json=self._generate_post_data(),
                         headers=h)
        
    def random_user_mail(self) -> str:
        return random.choice(self.student_mails)
    
    def random_user_id(self) -> int:
        return random.randint(1, self.counter)
    
    def on_stop(self):
        for email in self.student_mails:
            self.client.delete(url=f"{self.base_path}/by-email/{email}", headers=h)

    def _generate_post_data(self) -> dict:
        request_data = {
            "name": self.generate_random_string(),
            "email": f"{self.generate_random_string()}@{self.generate_random_string()}.{self.generate_random_string()}",
            "dob": self.generate_random_dob()
        }

        self.student_mails.append(request_data["email"])
        self.counter += 1

        return request_data
    
    def _generate_put_data(self) -> (str | dict):
        request_data = {
            "name": self.generate_random_string(),
            "email": f"{self.generate_random_string()}@{self.generate_random_string()}.{self.generate_random_string()}",
            "dob": self.generate_random_dob()
        }

        # select random email on db
        random_student_mail_on_db = random.choice(self.student_mails)
        # delete randomly selected email from list
        self.student_mails.remove(random_student_mail_on_db)
        # add new email to the list
        self.student_mails.append(request_data["email"])

        return f"{self.base_path}/{random_student_mail_on_db}", request_data

内容的提问来源于stack exchange,提问作者Serdar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 01:47:17