This article was first published on:Walker AI

Due to the company’s current demand for performance testing, several popular pressure measuring tools were investigated. As Jmeter and LoadRunner realize concurrency based on multithreading, which is determined by the operating system, it is difficult for a single machine to produce a large number of concurrent threads due to frequent context switching and frequent kernel scheduling. Running in multi-threaded mode will have a lot of overhead of thread switching and lead to waste of resources, so it is considered to use multi-coroutine. Jmeter is written in Java language, but Java does not support coroutine mechanism. The Python language implements coroutines in async/await mode, and Locust is based on Python. So this time I focused on the Locust performance testing tool.

First of all, we have to understand that only create pressure to evaluate the performance of the project, performance testing is the key to produce a large number of concurrent through tools, and the principle of concurrent strength depends on tool because Locust is a lightweight, script design more flexible, a lot of heavy pressure measuring tool can realize the function can also be implemented in the Locust. Locust in Python can achieve high concurrency in a more lightweight way, so I’ll show you how to get started quickly with important features and code examples.

1. Characteristics of Locust

(1) Develop scripts based on Python; (2) Open source, free, can be developed twice; (3) Distributed execution. Configure master and slave(master and slave machines) to continuously send requests to the system on multiple machines. (4) Event-driven. Unlike other tools that use processes and threads to simulate users, Locust can achieve orders of magnitude concurrency with the support of coroutines in the GEvent library; (5) It does not support monitoring of the machine under test and needs to cooperate with the assistance of other tools; (6) In the Locust class, there is a client attribute, corresponding to the virtual user as the client has the ability to request, that is, we often call the request method; Therefore, when using Locust, you need to inherit the Locust class first, and then bind the client implementation class in the client attribute of the inherited subclass. (7) HttpUser uses requests.Session, so all subsequent tasks will be logged in; (8) Version changes: Updates after 1.0 focus on replacing HttpLocust with Httpuser, task_set task sets need to be of list type, and task_set needs to be changed to Tasks.

2. Indicator system and common usage process of Locust

(1) Response time: it is a measure of the processing efficiency of the system from the beginning to the completion of a certain work. Response time usually increases with the increase of the load; (2) Throughput: an indicator reflecting the processing capacity of the system. It refers to the measurement of the work completed per unit time. It can be comprehensively evaluated from the perspective of client or server. (3) Transaction processing capability (TPS refers to RPS in LOCUST) : it refers to the corresponding situation of processing a transaction, usually including three indicators: the response time of processing the transaction, the success rate of processing the transaction, and the number of transactions that can be processed per unit time (per second, per minute, per hour, etc.).

The CMD command executes the script

Operations on the Web interface (you need to manually stop the web interface instead of automatically stopping it). Go to the project directory, py file level; Locust -f test.py or locust -f test.py –host=example.com; Open the browser to enter the Web interface and add the total number of simulated users and virtual users started per second. http://localhost:8089; Test result interface:

Pure command operation

Locust -f test.py –no-web -c 100 -r 20 -t 5 or locust -f test.py –host=example.com –no-web -c 100 -r 20 -t 5; -c: indicates the number of users. -r: number generated per second. -t: limits the running time. -n: total number of requests.

3. Locust syntax format

(1) Define a task class, the name of the class is arbitrarily defined; SequentialTaskSet () SequentialTaskSet () SequentialTaskSet (); (3) SequentialTaskSet is inherited when task requests are sequenced; (4) There is no order, can use the inheritance TaskSet class;

    import random
    from locust import HttpUser, task, between, SequentialTaskSet, tag
    class MyTaskCase(SequentialTaskSet) :
            Initialization method, equivalent to setup
            def on_start(self) :
                pass
    
        A decorator in Python that tells the following method to be a task,
        This decorator and the following method are copied many times, with a few changes, to write out multiple interfaces
        The number behind the decorator represents the proportion of tasks performed
        To use this decorator, you need to import tasks from the locust header
        @task
        @tag("leave_1")
        def regist_(self) :  The name of a method can be changed by itself
            url = '/erp/regist'  The URL requested by the interface
            Define the request header as a class variable so that it can be called by other tasks
            self.headers = {"Content-Type": "application/json"}
            self.username = "locust_" + str(random.randint(10000.100000))
            self.pwd = '1234567890'
            The request body of the post request
            data = {"name": self.username, "pwd": self.pwd}
            Select * from self.client; select * from self.client;
            # catch_response = True
            This parameter is optional. # name Task label name ----- This parameter is optional
            with self.client.post(url,
                                  json=data,
                                  headers=self.headers,
                                  catch_response=True) as rsp:
                if rsp.status_code > 400:
                    print(rsp.text)
                    rsp.failure('Regist_ interface failed! ')
    
        @task  # decorator, indicating that there is a task below
        def login_(self) :
            url = '/erp/loginIn'  The URL requested by the interface
            data = {"name": self.username, "pwd": self.pwd}
            with self.client.post(url,
                                  json=data,
                                  headers=self.headers,
                                  catch_response=True) as rsp:
                Extract the information from the response JSON and define it as a class variable
                self.token = rsp.json()['token']
                if rsp.status_code < 400 \
                        and rsp.json()['code'] = ="200":
                    rsp.success()
                else:
                    rsp.failure('Login_ interface failed! ')
    
        @task  # decorator, indicating that there is a task below
        def getuser_(self) :
            url = '/erp/user'  The URL requested by the interface
            # reference the class variable value of the previous task to implement parameter association
            headers = {"Token": self.token}  
           # use self.client to initiate a request
            with self.client.get(url, 
                                 headers=headers, 
                                 catch_response=True) as rsp: 
                if rsp.status_code < 400:
                    rsp.success()
                else:
                    rsp.failure('GetUser_ interface failed! ')
    
        # end method, equivalent to teardown
        def on_stop(self) :
            pass
    
    # define a runtime class that inherits the HttpUser class, so import the HttpUser class from locust
    class UserRun(HttpUser) :
        tasks = [MyTaskCase]
        We need to introduce between from locust
        wait_time = between(0.1.3)  
Copy the code

4. Single-port pressure test example

     #! /usr/bin/env python
    # -*- coding: utf-8 -*-
    from locust import task,TaskSet,HttpUser
    
    class UserTasks(TaskSet) :
        The following is a task
        @task
        def getIndex(self) :
            Client is a TaskSet member, which is equivalent to a Request object
            self.client.get("/path")
            print("here")
    class WebUser(HttpUser) :
        Which class is the set of tasks to execute
        tasks = [UserTasks]
        The interval between minimum wait time and maximum wait time requests
        min_wait = 1000
        max_wait = 2000
Copy the code

Run:

Enter: Locust -f Locust file to be executed. Py –host= http://domain name or IP port address of the server to be tested. For example, locust -f locust_test.py –host=http://localhost:8082; When the command is executed successfully, a message is displayed indicating the service port, for example: * : 8089. At this point, you can access the machine IP :8089 through the browser, see the task test page;

Number of Total Users to SIMULATE the Spawn rate (Users SPAWNED /second) Specifies the users generated per second

5. Service case pressure test example

The following uses a login interface and an interface to obtain an ID as an example

   # #! /usr/bin/env python
   # # -*- coding: utf-8 -*-
   from locust import task,TaskSet,HttpUser,tag
   from random import randint
   import json
   class UserTasks(TaskSet) :
   
       def on_start(self) :
       Prepare to test. Requests are accepted mostly in dictionary format
           self.loginData = [{"username":"1"."paswordword":"1"},
                             {"username":"2"."paswordword":"2"},
                             {"username":"3"."paswordword":"3"}]
       print("-----------on_start----------------")
   
       The following is a login task
       @task(1)
       def doLogin(self) :
           ranIndex = randint(1.len(self.loginData))
           Send the request and respond
           response = self.client.post("/path",data = self.loginData[ranIndex],catch_response=True)
           if "login-pass" in response.text:
               response.success()
           else:
               response.failure("Can not login!")
   
       The following is a task to get goods
       @task
       def get_goods(self) :
           body = {"good_name" :"apple"}
           Send the request and respond
           response = self.client.post("/path2",data = body,catch_response=True)
           newText = json.loads(response.txt)
           if "game_id" in newText[0].keys():
               response.success()
           else:
               response.failure("Can not get!")
   	
   	Assign weight to tasks
   	tasks = {doLogin:1, get_goods:2}
   	
   -e \ --exclude-tags can be used to exclude a tag from execution
   class WebUser(User) :
       @task
   	@tag("tag1"."tag2")
   	def my_task(self) :
       	pass

   class WebUser(HttpUser) :
       Tasks in the task set will be executed according to the assigned weight of 1:2
       tasks = [UserTasks]
       The interval between minimum wait time and maximum wait time requests
       min_wait = 1000
       max_wait = 2000

   # locust -f locust_test.py --host=http://localhost:8082
   # Number of total users to simulate the Number of users
   Spawned /second # Spawn Rate (Users SPAWNED /second) Specifies the number of users generated per second
Copy the code

Note: If the request value of the task interface requires parameters from the return value of the other interface, these non-task requests will also be displayed in the Statistics panel of locUST. To focus only on task interface statistics, dependent requests need to use the native Requests library.

6. Recommended data monitoring tools

(1) If the company has built a monitoring system, please ask operation and maintenance assistance to check it on the platform, such as Grafana; (2) Linux detection tool Nmon; (3) Windows comes with Perfmon; (4) Use Python’s PSUil library to customize detection frequency and index parameters, which require separate data processing;

7. To summarize

Compared with Jmeter and other tools, LocUST has much more freedom based on Python. It can customize the implementation method of special protocols. Moreover, locUST is easier to construct an automated performance test platform based on coroutines. Using LocUST is a wise choice.


PS: more dry technology, pay attention to the public, | xingzhe_ai 】, and walker to discuss together!