Locust understand and analyze source code

Foreword

I believe that many small partners will choose as the Locust pressure measurement tool auxiliary test paper from Locust source start analyzing its pros and cons, the conclusion in the end, I finally chose Jmeter

It analyzes the source code of Locust two files: main.py and runners.py

 

main.py

It executes program entry, the following code contains only the core code.

parse_options()

It is used to parse the incoming parameters, which can be read Locust find out what parameters are accepted and generally what role.

"" " 
The Handle with the Command-Line Options optparse.OptionParser. 

The Return of arguments The List, largely for use in` parse_arguments`. 
"" " 

# The Initialize, -H --host and are the same, the default is None, help tips are
 parser = OptionParser(usage="locust [options] [LocustClass [LocustClass2 ... ]]") parser.add_option(
  
'-H', '--host', dest="host", default=None, help="Host to load test in the following format: http://10.21.32.33" )
 

find_locustfile(locustfile) 和 load_locustfile(path)

  Role: to find and load our handwritten locust use cases, namely -f incoming files, ending py.
  Core code (excerpt):

# Perform the import (trimming off the .py)
imported = __import__(os.path.splitext(locustfile)[0])
# Return our two-tuple
locusts = dict(filter(is_locust, vars(imported).items()))

Above are: 1. The introduction locust with its import file cases. Obtained in Example 2. The use wherein the class. is_locust Boolean return type of a method for determining whether inherited TaskSet.

 

main()

  Long conditional branch, to take a different logic code in accordance with the input parameters.

  options object representing the parameters passed.
  locusts object represents our use cases TaskSet class.

  • If the --run-time parameters, the following code is called, the call is performed coroutine
def timelimit_stop():
    logger.info("Time limit reached. Stopping Locust.")
    runners.locust_runner.quit()
gevent.spawn_later(options.run_time, timelimit_stop)
Use the coroutine execution.
  • If there is no no-web parameters:
main_greenlet = gevent.spawn(web.start, locust_classes, options)

Also with a coroutine, launched a web program, the flask itself.
locust_classes and options is a web program parameters, including the host port.

  • If a master
# spawn client spawning/hatching greenlet
if options.no_web:
    runners.locust_runner.start_hatching(wait=True)
    main_greenlet = runners.locust_runner.greenlet
if options.run_time:
    spawn_run_time_limit_greenlet()

It will perform the corresponding master runners, hatching is hatching, which has started.
main_greenlet body is coroutine. Coroutines pond, Group (), I understand the many tasks of a similar collection (from gevent.pool import Group).
Coroutine not explain, a main_greenlet is the subject of a coroutine here, as you are the best 4-core CPU is four coroutines, which is defined and started four slave implementation, the code will not judge them.
runners.locust_runner content is another important document, later to explain.

Following the code is very similar.
master runner and slave runner LocustRunner inherit the class, wherein the method is implemented.

 

events.py

Locust framework of events, in simple terms, is to declare a method to the specified events in.
As long as the same method (various parameters), the events can be added to the.
After calling events of fire (self, ** kwargs), before calling the method declaration defines the complete trigger action.

class EventHook(object):
    """
    Simple event class used to provide hooks for different types of events in Locust.

    Here's how to use the EventHook class::

        my_event = EventHook()
        def on_my_event(a, b, **kw):
            print "Event was fired with arguments: %s, %s" % (a, b)
        my_event += on_my_event
        my_event.fire(a="foo", b="bar")
    """

    def __init__(self):
        self._handlers = []

    def __iadd__(self, handler):
        self._handlers.append(handler)
        return self

    def __isub__(self, handler):
        self._handlers.remove(handler)
        return self

    def fire(self, **kwargs):
        for handler in self._handlers:
            handler(**kwargs)

# 一个例子
request_success = EventHook()

Example codes used:

# register listener that resets stats when hatching is complete
def on_hatch_complete(user_count):
    self.state = STATE_RUNNING
    if self.options.reset_stats:
        logger.info("Resetting stats\n")
        self.stats.reset_all()
events.hatch_complete += on_hatch_complete
 

Above, events.hatch_complete task chain corresponding to a trigger (+ = add tasks to use).
Use the following code to call:

events.hatch_complete.fire(user_count=self.num_clients)


runners.py

weight_locusts(self, amount, stop_timeout = None)  

Calculated according to the weight to be used the number of users

DEF weight_locusts (Self, AMOUNT, STOP_TIMEOUT = None):
     "" " 
    Distributes The AMOUNT of locusts for each WebLocust-class ACCORDING to IT apos weight 
    Returns A List" bucket "with The Weighted locusts 
    " "" 
        # return value is an array, load replication the use of a pressure request 
    bucket = []
         # weight_sum weight is integrated for all values of the embodiment, weight value of the weight representation. 
    = SUM weight_sum ((locust.weight for Locust in self.locust_classes IF locust.task_set))
         # can have multiple use cases. 
    for Locust in self.locust_classes:
                 # Some judges ignored 
        if Not locust.task_set: 
            warnings.warn ( " Notice:. Locust class Found (% S) GOT NO task_set Skipping ... " .% Locust the __name__ )
             Continue 

        IF self.host IS  Not None: 
            locust.host = self.host
         IF STOP_TIMEOUT iS  not None: 
            locust.stop_timeout = STOP_TIMEOUT 

        # the Create locusts weight DEPENDING oN 
                # in a loop that is a use case, percent means that the proportion of patients with total weights of. 
        locust.weight = Percent / a float (weight_sum)
                # For example users 1000 is provided, according to the weight ratio of weights is calculated in 1000 the number of users the user to execute the use cases. 
        int = num_locusts (round (AMOUNT * Percent))
                 # copied and added to the result set 
        bucket.extend ([Locust for X in xrange (0, num_locusts)])
     return bucket

 

spawn_locusts(self, spawn_count=None, stop_timeout=None, wait=False)

Use of sleep to achieve the effect of the number of users per second run.

def spawn_locusts(self, spawn_count=None, stop_timeout=None, wait=False):
    if spawn_count is None:
        spawn_count = self.num_clients

    # 计算后的用户数,实际执行的用户数。
    bucket = self.weight_locusts(spawn_count, stop_timeout)
    spawn_count = len(bucket)
    if self.state == STATE_INIT or self.state == STATE_STOPPED:
        self.state = STATE_HATCHING
        self.num_clients = spawn_count
    else:
        self.num_clients += spawn_count
  # hatch_rate 的解释:The rate per second in which clients are spawned. Only used together with --no-web
    logger.info("Hatching and swarming %i clients at the rate %g clients/s..." % (spawn_count, self.hatch_rate))
    occurence_count = dict([(l.__name__, 0) for l in self.locust_classes])

     # 定义执行的方法
    def hatch():
        sleep_time = 1.0 / self.hatch_rate
        while True:
            if not bucket:
                logger.info("Locusts hatched All:% S " % " , " .join ([ " % S:% D " % (name, COUNT) for name, COUNT in six.iteritems (occurence_count)])) 
                events.hatch_complete.fire (USER_COUNT = self.num_clients)
                 return 

                    # use cases pop 
            Locust = bucket.pop (the random.randint (0, len (bucket) -1 )) 
            occurence_count [Locust. the __name__ ] +. 1 =
                     # methods defined start, the implementation can be seen run () method 
            DEF start_locust (_):
                 the try :
                    Locust () RUN (). 
                the except GreenletExit:
                     Pass 

                    # performing the method coroutine, also Group () of the spawn 
            new_locust = self.locusts.spawn (start_locust, Locust)
             IF len (self.locusts)% 10 == 0: 
                Logger .debug ( " % i locusts hatched " % len (self.locusts))
                     # sleep that is waiting for a specified time. 
            gevent.sleep (sleep_time) 

    Hatch () 
    IF the wait: 
        self.locusts.join () 
        logger.info ( " All locusts Dead \ the n- " )

kill_locusts(self, kill_count)

  1. In accordance with the weights calculated how many users want to get rid of.
  2. stopped by the user get rid of the coroutine pond, and ejected from the weight pond.

bucket = self.weight_locusts(kill_count)
kill_count = len(bucket)
self.num_clients -= kill_count
logger.info("Killing %i locusts" % kill_count)
dying = []
for g in self.locusts:
    for l in bucket:
        if l == g.args[0]:
            dying.append(g)
            bucket.remove(l)
            break
for g in dying:
    self.locusts.killone(g)
# 收尾工作,主要是提示给页面和打日志
events.hatch_complete.fire(user_count=self.num_clients)

 

Some features of Locust and thinking, contrast and Jmeter

Performance tests are conducted know Jmeter is not open around the tool, then the Locust and its advantages and disadvantages compared to what?

  
  Jmeter updated almost every day, Locust almost nothing to update.

  Locust front end is implemented in the chart.js, LocustLineChart, is quite simple.
  Jmeter can install the plug-in displays, but also simple.

  

  Jmeter also install plug-ins to achieve end service performance monitoring, simple.
  Locust do not.

 

  Locust is no test report.
  Jmeter3.0 began to support report generation, but flawed.

Part test:

python script is a bright spot, after all, can achieve all the code requirements.
But the downside is obvious:
1.util package is not complicated use cases to write a lot of code work, maintenance costs a lot, but the skill test the code.
2. There is no recorded use cases, use cases save function, even with the support of HttpRunner save the recording, only the base case.
In fact, such as performance testing just to be parameterized, or to a python script handwriting.
For more tight timelines test requirements, with Locust obviously hit the wall.

Jmeter obviously a lot of good, simple GUI interface itself, a variety of built-in functions to help you write the script.
Even if written in very complex cases, also it provides beanshell, you can use Java code implementation (although debugging more strenuous).
Meanwhile Jmeter has a variety of protocol plug-ins, it is good.

Concurrency

Locust using four slave, is caused by the pressure 1.3k, Jmeter is 13k, a difference of 10 times.

Locust pressure side as the RPS too weak after final experiments concluded that monocytes can carry only approximately 500

 

Summary: Use Locust be careful, be careful.

Guess you like

Origin www.cnblogs.com/Ronaldo-HD/p/11652657.html