【性能测试】之python locust

locust是什么

locust翻译成中文是“蝗虫“,蝗虫通常数量庞大,繁殖能力很强。

locust是一款开源的用python实现的性能/压力测试工具,可以通过命令行或自带的WEB-GUI启动压测。locust会将response_time,失败数实时地统计出来,方便后续分析

locust安装

locust是python的依赖包,可以通过pip install locust直接安装;locust官方文档:https://docs.locust.io/en/stable/installation.html

安装成功后可以通过locust -V命令查看,如果返回“locust 2.18.3 from /xx/lib/python3.9/site-packages/locust (python 3.9.10)”

locust --help可以查看命令行支持的参数

locust使用

简易的例子

from locust import TaskSet, task, HttpUser, between, events
from locust.runners import MasterRunner

@events.init.add_listener
def on_locust_init(environment, **kwargs):
    if isinstance(environment.runner, MasterRunner):
        print("I'm on master node")
    else:
        print("I'm on a worker or standalone node")

@events.test_start.add_listener
def on_test_start(environment, **kwargs):
    print("A new test is starting")

@events.test_stop.add_listener
def on_test_stop(environment, **kwargs):
    print("A new test is ending")

class WebsiteTasks(TaskSet):
    def on_start(self):
        print("on_start")

    def on_stop(self):
        print("on_stop")
	@tag("B")
    @task
    def index(self):
        self.client.get("/")
        # with self.client.get("/", catch_response=True) as response:
        #     logger.debug(response.text)
        #     # logger.debug(response.json())
        #     try:
        #         if response.status_code != 200 or response.json()['result_code'] != "success":
        #             response.failure(f"status_code: {response.status_code} reason: {response.reason} response: {response.text}")
        #         else:
        #             response.success()
        #     except Exception as e:
        #         response.failure(f"reason: 服务未响应 {e}  {response.status_code}")

	@tag("A")
    @task
    def about(self):
        self.client.get("/hello")
        # with self.client.get("/hello", catch_response=True) as response:
        #     # logger.debug(response.json())
        #     try:
        #         if response.status_code != 200 or response.json()['result_code'] != "success":
        #             response.failure(f"status_code: {response.status_code} reason: {response.reason} response: {response.text}")
        #         else:
        #             response.success()
        #     except Exception as e:
        #         response.failure(f"reason: 服务未响应 {e}  {response.status_code}")

class WebsiteUser(HttpUser):
    host = "http://localhost:5000"
    tasks = [WebsiteTasks]
    # min_wait = 1000
    # max_wait = 5000
    wait_time = between(1, 2)
    # environment = {"key": "value"}

WebsiteUser

WebsitteUser继承自HttpUser,而HttpUser继承自User。User类有些必须设置的参数

# User类源码
class User(metaclass=UserMeta):
    """
    Represents a "user" which is to be spawned and attack the system that is to be load tested.

    The behaviour of this user is defined by its tasks. Tasks can be declared either directly on the
    class by using the :py:func:`@task decorator <locust.task>` on methods, or by setting
    the :py:attr:`tasks attribute <locust.User.tasks>`.

    This class should usually be subclassed by a class that defines some kind of client. For
    example when load testing an HTTP system, you probably want to use the
    :py:class:`HttpUser <locust.HttpUser>` class.
    """

    host: Optional[str] = None
    """Base hostname to swarm. i.e: http://127.0.0.1:1234"""

    min_wait = None
    """Deprecated: Use wait_time instead. Minimum waiting time between the execution of locust tasks"""

    max_wait = None
    """Deprecated: Use wait_time instead. Maximum waiting time between the execution of locust tasks"""

    wait_time = constant(0)
    """
    Method that returns the time (in seconds) between the execution of locust tasks.
    Can be overridden for individual TaskSets.

    Example::

        from locust import User, between
        class MyUser(User):
            wait_time = between(3, 25)
    """

    wait_function = None
    """
    .. warning::

        DEPRECATED: Use wait_time instead. Note that the new wait_time method should return seconds and not milliseconds.

    Method that returns the time between the execution of locust tasks in milliseconds
    """

    tasks: List[TaskSet | Callable] = []
    """
    Collection of python callables and/or TaskSet classes that the Locust user(s) will run.

    If tasks is a list, the task to be performed will be picked randomly.

    If tasks is a *(callable,int)* list of two-tuples, or a {callable:int} dict,
    the task to be performed will be picked randomly, but each task will be weighted
    according to its corresponding int value. So in the following case, *ThreadPage* will
    be fifteen times more likely to be picked than *write_post*::

        class ForumPage(TaskSet):
            tasks = {ThreadPage:15, write_post:1}
    """

    weight = 1
    """Probability of user class being chosen. The higher the weight, the greater the chance of it being chosen."""

    fixed_count = 0
    """
    If the value > 0, the weight property will be ignored and the 'fixed_count'-instances will be spawned.
    These Users are spawned first. If the total target count (specified by the --users arg) is not enough
    to spawn all instances of each User class with the defined property, the final count of each User is undefined.
    """

    abstract = True
    """If abstract is True, the class is meant to be subclassed, and locust will not spawn users of this class during a test."""

    def __init__(self, environment):
        super().__init__()
        self.environment = environment
        """A reference to the :py:class:`Environment <locust.env.Environment>` in which this user is running"""
        self._state = None
        self._greenlet: greenlet.Greenlet = None
        self._group: Group
        self._taskset_instance: TaskSet = None
        self._cp_last_run = time.time()  # used by constant_pacing wait_time

    def on_start(self):
        """
        Called when a User starts running.
        """
        pass

    def on_stop(self):
        """
        Called when a User stops running (is killed)
        """
        pass

    @final
    def run(self):
        self._state = LOCUST_STATE_RUNNING
        self._taskset_instance = DefaultTaskSet(self)
        try:
            # run the TaskSet on_start method, if it has one
            try:
                self.on_start()
            except Exception as e:
                # unhandled exceptions inside tasks are logged in TaskSet.run, but since we're not yet there...
                logger.error("%s\n%s", e, traceback.format_exc())
                raise

            self._taskset_instance.run()
        except (GreenletExit, StopUser):
            # run the on_stop method, if it has one
            self.on_stop()

    def wait(self):
        """
        Make the running user sleep for a duration defined by the User.wait_time
        function.

        The user can also be killed gracefully while it's sleeping, so calling this
        method within a task makes it possible for a user to be killed mid-task even if you've
        set a stop_timeout. If this behaviour is not desired, you should make the user wait using
        gevent.sleep() instead.
        """
        self._taskset_instance.wait()

    def start(self, group: Group):
        """
        Start a greenlet that runs this User instance.

        :param group: Group instance where the greenlet will be spawned.
        :type group: gevent.pool.Group
        :returns: The spawned greenlet.
        """

        def run_user(user):
            """
            Main function for User greenlet. It's important that this function takes the user
            instance as an argument, since we use greenlet_instance.args[0] to retrieve a reference to the
            User instance.
            """
            user.run()

        self._greenlet = group.spawn(run_user, self)
        self._group = group
        return self._greenlet

    def stop(self, force=False):
        """
        Stop the user greenlet.

        :param force: If False (the default) the stopping is done gracefully by setting the state to LOCUST_STATE_STOPPING
                      which will make the User instance stop once any currently running task is complete and on_stop
                      methods are called. If force is True the greenlet will be killed immediately.
        :returns: True if the greenlet was killed immediately, otherwise False
        """
        if force or self._state == LOCUST_STATE_WAITING:
            self._group.killone(self._greenlet)
            return True
        elif self._state == LOCUST_STATE_RUNNING:
            self._state = LOCUST_STATE_STOPPING
            return False
        else:
            raise Exception(f"Tried to stop User in an unexpected state: {self._state}. This should never happen.")

    @property
    def group(self):
        return self._group

    @property
    def greenlet(self):
        return self._greenlet

    def context(self) -> Dict:
        """
        Adds the returned value (a dict) to the context for :ref:`request event <request_context>`.
        Override this in your User class to customize the context.
        """
        return {}

    @classmethod
    def fullname(cls) -> str:
        """Fully qualified name of the user class, e.g. my_package.my_module.MyUserClass"""
        return ".".join(filter(lambda x: x != "<locals>", (cls.__module__ + "." + cls.__qualname__).split(".")))

  • host: 用于指明压测系统的host,即//hello的host_prefix,作用等用于locust -H http://localhost:5000
  • tasks: locust框架将要执行的测试case
  • wait_time:单位是s,locust user执行间的sleep_time
    max_wait、min_wait单位是ms,wait_function也可以实现同样的目的,不过在最新版本中已弃用
    wait_time可以通过constant设置为常量,也可以通过between设置为指定范围的随机值
    
  • environment:

WebsiteTasks

WebsiteTasks继承自locust.TaskSet类,压测case通过task装饰器来修饰。在启动压测任务时,locust会分配协程来执行这些task

  1. task装饰器支持传参,@task(3)表明这个task的weight=3,后续locust会根据这个权重去执行task;如果未指明weight,则locust随机执行
  2. WebsiteTask的self.client指向webSiteUser继承自HttpUser的client,而HttpUser.client是封装好的HttpSession,可以通过self.client发起http请求。
  3. 可以通过with关键字和catch_response=True 获取http请求的response,支持自定义验证response;执行失败使用response.failure()标记,执行成功使用response.success()标记
    @task
    def index(self):
        with self.client.get("/", catch_response=True) as response:
            logger.debug(response.text)
            # logger.debug(response.json())
            try:
                if response.status_code != 200 or response.json()['result_code'] != "success":
                    response.failure(f"status_code: {response.status_code} reason: {response.reason} response: {response.text}")
                else:
                    response.success()
            except Exception as e:
                response.failure(f"reason: 服务未响应 {e}  {response.status_code}")
    
  4. on_start和on_stop方法既可以在TaskSet中重写,也可以在HttpUser中重写。作用是当每个user开始执行task是先调用on_start方法,当user执行完task后再调用on_stop方法
  5. tag装饰器支持传参,可以通过命令行–tags 或者 --exclude-tags来筛选task

Event

event 提供了模块级别的setUp和tearDown

  1. test_start和test_stop方法:@events.test_start.add_listener可以作为模块级别的setUp 使用;@events.test_stop.add_listener可以作为模块级别的tearDown 使用;
  2. init方法:在locust进程启动前可以触发;尤其是在多workers下,可以通过environment参数对worker进行初始化操作
  3. other event:https://docs.locust.io/en/stable/extending-locust.html#extending-locust
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值