8.性能测试工具Locust的高级使用——熟悉相关的类和方法、特性

载*请注明原始出处:http://blog.csdn.net/a464057216/article/details/48394213

后续此博客不再更新,欢迎大家搜索关注微信公众号“测开之美”,测试开发工程师技术修炼小站,持续学习持续进步。
在这里插入图片描述

学会了Locust工具的基本使用方法后,可以更深入的了解下它所有的类及对应的方法和属性。

每一个模拟的用户可以看做一个Locust类的实例,Locust类有如下属性:
task_set = None:指向一个TaskSet类,TaskSet类定义了每个用户的行为。
min_wait = 1000:用户执行任务之间等待时间的下界,单位:毫秒。如果TaskSet类中有覆盖,以TaskSet中的定义为准。
max_wait = 1000:用户执行任务之间等待时间的上界,单位:毫秒。如果TaskSet类中有覆盖,以TaskSet中的定义为准。
host = None:如果是Web服务的测试,host相当于是提供URL前缀的默认值,但是如果在命令行中指定了-H选项,则以命令行中指定的为准。如果不是Web服务测试,使用默认的None就好。
weight = 10:一个Locust实例被挑选执行的权重,数值越大,执行频率越高。在一个locustfile.py文件中可以同时定义多个Locust子类,然后分配他们的执行权重,例如:

from locust import Locust, TaskSet, task

class UserTask(TaskSet):
    @task
    def job(self):
        pass

class UserOne(Locust):
    weight = 1
    task_set = UserTask

class UserTwo(Locust):
    weight = 2
    task_set = UserTask

然后在终端执行:

mars@mars-Ideapad-V460:~/test$ locust -f locustfile3.py UserOne UserTwo
[2015-09-12 15:11:20,722] mars-Ideapad-V460/INFO/locust.main: Starting web monitor at *:8089
[2015-09-12 15:11:20,766] mars-Ideapad-V460/INFO/locust.main: Starting Locust 0.7.3

那么UserTwo的执行频率在统计上会是UserOne的两倍。

正如字面意思,TaskSet类定义了每个用户的任务集合,测试任务开始后,每个Locust用户会从TaskSet中随机挑选(如果定义了任务间的权重关系,那么就是按照权重关系随机挑选)一个任务执行,然后随机等待Locust类中定义的min_wait和max_wait(如果TaskSet类中也定义了min_wait或者max_wait,按照TaskSet中的为准)之间的一段时间,执行下一个任务。
定义TaskSet中的任务有多种方式,比如使用@task修饰符、使用tasks属性。
使用@task修饰符的方法在前一篇文章中有介绍,另外也可以在后面指定一个数值作为权重,例如:

from locust import Locust, TaskSet, task

class UserTask(TaskSet):
    @task(1)
    def job1(self):
        print 'This is job 1'

    @task(2)
    def job2(self):
        print 'This is job 2'

class User(Locust):
    task_set = UserTask

上面的代码中,job2任务的执行频率统计上来看会是job1的二倍。
使用tasks属性的方法在前一篇文章中也有介绍,需要注意的是TaskSet的定义是可以嵌套的,因为考虑到现实中有很多任务其实也是有嵌套结构的,比如:

from locust import Locust, TaskSet, task

class UserTask(TaskSet):
    @task(2)
    class stay(TaskSet):
        @task(3)
        def readBook(self):
            print 'I am reading a book.'

        @task(7)
        def listenMusic(self):
            print 'I am listening to music.'

        @task(1)
        def logOut(self):
            self.interrupt()

    @task(1)
    def leave(self):
        print 'I don not like this page.'

class User(Locust):
    task_set = UserTask

上面的例子中,要么用户不喜欢这个网页直接离开,要么喜欢就留下来,留下来的话,可以选择看书、听音乐、或者离开。在stay这个类中,对interrupt()方法的调用是非常重要的,这可以让一个用户跳出stay这个类有机会执行leave这个任务,否则他一旦进入stay任务就会一直在看书或者听音乐而难以自拔。
除了使用@task修饰符完成嵌套,也可以使用tasks属性,效果是一样的:

from locust import Locust, TaskSet, task

class stay(TaskSet):
    @task(3)
    def readBook(self):
        print 'I am reading a book.'

    @task(7)
    def listenMusic(self):
        print 'I am listening to music.'

    @task(1)
    def logOut(self):
        self.interrupt()

class UserTask(TaskSet):
    tasks = {stay:2}

    @task(1)
    def leave(self):
        print 'I don not like this page.'

class User(Locust):
    task_set = UserTask

TaskSet类还有其他的有用方法:

  • on_start()函数
    定义每个locust用户开始做的第一件事。

  • locust属性
    指向每个TaskSet所属的loucst用户实例。

  • parent属性
    指向每个TaskSet所属的父类TaskSet,用在TaskSet有嵌套的情况,如果调用parent的TaskSet是最顶层的,则返回它所属的locust用户实例。

  • client属性
    指向TaskSet所属的父HttpLocust类的client属性,self.client与self.locust.client效果是一样的。如果TaskSet所属的父类是个Locust类,则没有这个client属性。

  • interrupt(reschedule=True)
    顶层的TaskSet(即被绑定到某个Locust类的task_set的第一层TaskSet)不能调用这个方法。reschedule置为True时,从被嵌套任务出来马上选择新任务执行,如果置为False,从被嵌套任务出来后,随机等待min_wait和max_wait之间的一段时间,再选择新任务执行。

  • schedule_task(task_callable, args=None, kwargs=None, first=False)
    将一个可调用的对象task_callable添加进Locust对象(注意是针对某个Locust实例,而不是所有的Locust实例)的任务选择队列,其中args和kwargs是传递给可调用对象的参数,如果first置为True,则将其加到队首。

  • HttpLocust

Locust原本是为了对Web服务进行性能测试而生,HttpLocust类继承自Locust类,可以方便发送HTTP请求。采用HttpLocust类时会在网页的统计UI上显示实时统计信息,如果是Locust类则不会显示。HttpLocust类比Locust类多了client特性,client特性中包含locust.clients.HttpSession类的实例,支持cookie,能在请求之间保持session。
HttpSession其实是requests.Session的子类,支持HTTP的各种请求,如get、post、put、delete、head、patch、options,同时发送统计信息给Web界面。下面举个get请求的例子:

from locust import HttpLocust, TaskSet, task

class UserTask(TaskSet):
    @task
    def job(self):
        response = self.client.get('/')
        print 'Status code: %s' % response.status_code
        print 'Content : %s' % response.content

class User(HttpLocust):
    task_set = UserTask
    min_wait = 1000
    max_wait = 1000

其实ResponseContextManger类是Response类的子类,只是多了两个failure()和success()方法,使用方法如下:

from locust import HttpLocust, TaskSet, task

class UserTask(TaskSet):
    @task
    def job(self):
        with self.client.get('/', catch_response = True) as response:
            if response.status_code == 200:
                response.failure('Failed!')
            else:
                response.success()

class User(HttpLocust):
    task_set = UserTask
    min_wait = 1000
    max_wait = 1000

上面的例子比较无聊(只是为了说明使用with语句及catch_response参数可以截获原始响应),把所有status_code是200的响应都当做失败响应。注意,这里success()和failure(str)的调用会体现在结果的统计上,例如:
这里写图片描述

如果点开Failures查看详细信息,会看到之前我们上报上的错误信息“Failed!”:
这里写图片描述

另外,failure方法的参数除了可以是字符串以外,还可以是python的exception类。

有的时候某个请求的url会接受变化的参数,比如/blog?id=id,其中斜体的id如果有一千个,那么在统计网页上会显示一千行,看这样的统计信息也没有意义(当然,有时候可能是有用的),我们可以在get请求中加个name参数来将这些统计叠加到一起,比如:

from locust import HttpLocust, TaskSet, task

class UserTask(TaskSet):
    @task
    def job(self):
        self.client.get('/', name = 'Test')
        self.client.get('/s?ie=utf-8&f=8&rsv_bp=0&rsv_idx=1&tn=baidu&wd=a&rsv_pq=ea402fde00059549&rsv_t=e66eMrX2jdLCr1nxf9CP4BE0P7GnDSeU4MqgeDc3Yl5ooSsZ32efVWA1AUY&rsv_enter=1&rsv_sug3=1&rsv_sug1=1&rsv_sug2=0&inputT=285&rsv_sug4=286', name = 'Test')

class User(HttpLocust):
    task_set = UserTask
    min_wait = 1000
    max_wait = 1000

显示出来的统计信息如下:
这里写图片描述

HttpSession类与Python的requests.Session类十分相似,不同的地方在于:

  1. Locust会自动跟踪HTTP请求的状态并统计,如果不想使用默认的安全形式,可以配合with语句及catch_response参数捕捉原始响应。
  2. 可以使用name参数对不同URL的请求进行分类。
  3. 第一个参数不再是完整的URL,比如请求http://www.baidu.com/,只需要提供参数/即可,http://www.baidu.com是通过Locust类的host特性提供的,或者通过locust命令行的-H参数提供。

下面举一个使用HttpSession类的例子:

from locust import Locust, TaskSet
from locust.clients import HttpSession

def test(l):
    s = HttpSession('http://www.baidu.com')
    with s.get('/', catch_response = True, name = "mars_test") as r:
        print r.status_code

class UserTask(TaskSet):
    tasks = [test]

class User(Locust):
    task_set = UserTask
    min_wait = 3000
    max_wait = 5000

运行后的Web界面如下显示:
这里写图片描述

使用HttpSession类能够完成python的requests模块的delete、get、post、put、options、head、patch、request等请求,同时还有向Locust的Web界面发送统计信息及分组统计等特性。

Locust内部其实使用了python的requests模块发送HTTP请求,所以HTTP响应类的特性及方法也十分类似,具体也可以参考requests模块。

TaskSet类的interrupt方法就是通过抛出一个InterruptTaskSet实例实现的。

EventHook类在locust.events模块中,可以用于发生某些事情时的钩子函数,比如:

from locust import Locust, TaskSet, events, task

mars_event = events.EventHook()

def mars_special_event(verb = '', content = ''):
    print 'mars %s %s' % (verb, content)

mars_event += mars_special_event

class UserTask(TaskSet):
    @task(1)
    def job1(self):
        mars_event.fire(verb = 'love', content = 'locust')

    @task(3)
    def job2(self):
        print "In job2 ..."

class User(Locust):
    task_set = UserTask
    min_wait = 3000
    max_wait = 5000

除了我举得上面这个没有什么意义的例子之外,Locust还提供了如下7种有用的钩子:

  1. request_success:当请求完全成功时被触发,钩子函数需要定义如下参数:

request_type:请求的类型
name:请求的URL或者自定义的统计分组名字(如果请求时提供了name参数的话)
response_time:请求花费的时间(毫秒为单位)
response_length:响应长度

  1. request_failure:当请求失败时触发,钩子函数需要定义如下参数:

request_type:请求的类型
name:请求的URL或者自定义的统计分组名字(如果请求时提供了name参数的话)
response_time:请求花费的时间(毫秒为单位)
exception:请求失败抛出的异常

  1. locust_error:在Locust实例执行发生异常时触发,钩子函数需要定义如下参数:

locust_instance:发生异常的Locust类的实例
exception:抛出的异常
tb:来自sys.exc_info()[2]的Traceback对象

  1. hatch_complete:所有的Locust实例产生完成时触发,钩子函数需要定义如下参数:

user_count:产生的Locust实例(虚拟用户)的数量。

  1. quitting:Locust进程退出时被触发,钩子函数不需要提供参数。

现根据目前这5个钩子举一个例子:

from locust import HttpLocust, TaskSet, events, task
import random, traceback

def on_request_success(request_type, name, response_time, response_length):
    print 'Type: %s, Name: %s, Time: %fms, Response Length: %d' % \
            (request_type, name, response_time, response_length)

def on_request_failure(request_type, name, response_time, exception):
    print 'Type: %s, Name: %s, Time: %fms, Reason: %r' % \
            (request_type, name, response_time, exception)

def on_locust_error(locust_instance, exception, tb):
    print "%r, %s, %s" % (locust_instance, exception, "".join(traceback.format_tb(tb)))

def on_hatch_complete(user_count):
    print "Haha, Locust have generate %d users" % user_count

def on_quitting():
    print "Locust is quiting"

events.request_success += on_request_success
events.request_failure += on_request_failure
events.locust_error += on_locust_error
events.hatch_complete += on_hatch_complete
events.quitting += on_quitting


class UserTask(TaskSet):
    @task(5)
    def job1(self):
        with self.client.get('/', catch_response = True) as r:
            if random.choice([0, 1]):
                r.success()
            else:
                r.failure('0')

    @task(1)
    def job2(self):
        raise Exception("Mars Loo's test")

class User(HttpLocust):
    task_set = UserTask
    min_wait = 3000
    max_wait = 5000
    host = 'http://www.baidu.com'

其他两种钩子涉及到分布式使用Locust,请参考下一篇博客。

如果觉得我的文章对您有帮助,欢迎关注我(CSDN:Mars Loo的博客)或者为这篇文章点赞,谢谢!

  • 7
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值