本文转载自微信公众号「dongfanger」,作者dongfanger。转载本文请联系dongfanger公众号。
locustfile是什么?
locustfile是Locust性能测试工具的用户脚本,描述了单个用户的行为。
locust` ] e Nfile是个普通的Python模块,f 4 v U如果写作locustfj J R Sile.py,那么路径切换到文件所在目录,直接执行命令就能运行:
- $locust
如果换个名字x c P [ . `,那么只能通过-f参数指定文件名运行:
- $locust-flocust_files/my& y . ^ n ) 4 n L_locust_file.py
与一般Python模块不同的是y 5 j m \ 8 { h N:locustfile必须至少定义一个类,且继承自User类。N y x J : 1 x
User类
User类表示性能测试的模拟用户,Locust会在运行时创建User类的实例。
wait_ti5 ( Q : vme属性
设置等待时间,默认值不等待,立即执行。
Locu^ 3 } Ast支持4种方式设置wait_time属性。
为了便于理解实际v S 2 s u意义,我把源码贴在了下面。
- constant函数,常量,任务执行完毕等待X秒开始下一任务。
- defconstant(wait_td 2 \ X \ G a iime):
- """
- Returnsafunctionthatjustreturnsthenumberspecifiedbythewait_timeargument
- Example+ T = U::
- classMyUser(User):
- wait_time=constant(3)
- """
- returnlambdainstance:wait_time
- between函数,区{ u 7 R \ V % C w间随机值,任务执行完毕等待X-Y秒(中间随机取值)开始下一任务。
- defbetween(min_wait,max_wait):
- """
- Returnsafunctionthatwillreturnarandomnumberbetween\ 2 0min_waitandmax_wait.
- Example::
- classMyUser(UseD n 0 5 9 m 0 Or):
- #waitbetween3.0and10.5secondsaftereachtask
- wai[ Z @ ^ L G Jt_tim: h 4 ) ,e=between(3.0,10.5)
- """
- returnlambdainstance:mC \ [ t $ } P Z Sin_wait+random.random()*(max_wait-min_wait)
- constant_pacing函数,U K R v自适应,若任9 | G ^ Y K f D D务耗时超过该时间,则任务结束后立即执行下一任务;若任务耗时不超过该时间,则等待达到该时间后执行下一任务。
- defconstant_pacing(wait_time):
- """
- Returnsafunc) ; \ i i _ f - gtionthatwilltracktheruntimeofth% B R 6etasks,andforeachtimeit's
- calleditwill: k f w J Jreturnawaittimethatwillt# C 6 $rytomakethetotaltimebetweentask
- executionequaltothY N u ; *etimespecifiedbythe i u l - Y x O uewait_timeargument.
- Inthefollowingexamplethetaskwillalwaysbeexecutedon# k s .ceevery4 _ J N 1 ? V % /second,nomatter
- thet9 e Y 9 Vaskexecutiontime::
- clV o H d 6 L o T iassMyUser(User):
- wait_time=constant_pacingp q $(1)
- @task
- defmy_task(self):
- time.sA b @ & ? % 2 Wleep(random.random())
- Ifataskexecutionexceedsthespecifie; 0 = mdwait_time,thewaitwillbe0beforestarting
- thenexttask.
- """
- defwait_time_func(self):
- ifnothasattr(s5 + \ ? I I K Yelf,"_cp_lK 9 ! 5 # l M & kast_run"):
- self._cp_last_wait_time=waitv u Z W R t_time
- self._cp_last_run=time()
- returnwait_time
- else:
- run_time=timA D I q ) -e()-self._cp_last_run-self._cp_last_wait_time
- self._cp_last_i 1 ^ ] T - Mwait_tim; ( h P ue=max(0,wait_time-run_time)
- self._cp_last_run=time()
- returnself._cp_last_wait_time
- returnwait_time_func
- 自定义wait_time方法,比如每次等待时间1秒2秒3秒递增:
- classMyUser(User):
- last_wait_time=0
- defwait_time(self):
- self.las- o u 7t_wait_time+=1
- returnself.last_wait_time
- ...
weight属性
设置创建类实例的权重,默认每个类创建相同数量的实例。
locustfile中可以有多个继承了User类的类。
命令行可以s H 5指定运行哪些类:
- $locust-flocust_file.pyWebUserMobileUser
通过weight属性可以x $ ^ )让类更大概率创建实例,比如:
- classWebUser(User):
- weight=3
- ...
- classMobileUser(User):
- weighi | C = . # B ) (t=1
- ...
WebUser类比MobileUser类多三倍概率创建实例。
host属性
设置URL前缀。
一般是在Locust的Web UI或者命令行,通过–9 [ O U 4 D H ghost指定URL前缀。如果没有通过–0 @ h Z L x | Lhost指定,并且类中q ? o 3 +设置了hosti N g ~ –属性,那么类的host属E T D W @性才会p S X T生效。
envF U I $ e Z 9 r !ironmenO \ ; t Z z L G at属性
对用户运行环境的引用。
比如在task方法中通过environment属性^ t m 7终止运行:
- self.environment.runner.quit()! u T
注意,单机会终止所有运行,分布式只会终止单个worker节点。
oX U b . [ 5 ] \ ~n_start和on_stop方法
测试前初始化和测试后清理。
HttpUser类
开篇文章的示例脚本,没有继承User类,而是继承了它的子类HttpUse] n | 2 8r:
它比User类更常用,因为它添加了一个client属性,用来发送HTT( @ V jP请求。
clientv E % % a 3 % I k属性/HttpSession
Httpq ~ C NUser类的client属性是HttpSession类的一个实例:
HttpSession是rB y * n ) P sequestV T l N 3 X M gs.Session的子类,requests就是常用来做接O T ) W l %口测试的那个requests库:
HttpSession没有对requests.Session做什么改动,主要是传递请求结果给Locust,比如success/fail,response time,response length,name。
示例:
- response=self.client.post("/login",{"username":"testuserS c G s y","password":") Y \ ysecret"})
- prinq o \t("Responsestatuscode:",response.sta~ o % # Gtus_c8 6 X $ ? 6 vode)
- print("Responsetext:",response.text)
- response=self.client.get("/my-profile")
由U I x W于requests.Session会暂存cook: z b @ Lie,所以示例中登录/login请求后可以继续请求/5 _ e ~ A ~ zmy-profileE S n 4。
断言响应结果
可以使用with语句和catch_response参? y J B U – l {数对响应结果进行断言| D w:
- withself.client.get("/",catch_response=True)asresponse:
- ifresponse.text=="Success":
- rX 2 \ c Tesponse.success()
- elifresponse.text!="Success":
- response.failure("Gotwrongresponse")
- elifresponse.eli ~ S k 5 n 5apsed.total_seconds()>0.5( m o 6 x 4 ` P J:
- response.failure("Requesttooktoolong")
或者直接抛出异常:
- fromlocust.exceptionimpo4 x b G \ (rtRescheduleTask
- ...
- withself.client.get("/does_not_exist/",catch_response=True)asresponse:
- ifresponse.status_code==404:
- raiseRescheduleTask()
nI 9 \ J n ] Aame参数
name参数用于把不同apix o j L ` Q按同一分组进行统计,比如:
- foriinrange(10):
- self.client.get("/blog?$ ! q E w ` $ ~id=%i"%i,name="/blog?id=[id]")
会按/blog/?id=[id]统计1条数据,而不是分成10条数据。
H? C 2 p 8TTP代理
Locust默认设置了requests.Session的trust_env为FalsY M Y – . R F Ge,不查找代理,以提高运行性能。如果需要可以设置locust_instance.client.trust_env4 y I Z H k j H为True。
示例代码
请求REST API并断言H z I # I Z:
- fromjsonimportJSONDecodeError
- ...
- withself.client.post("/",json={4 ! 5"foo":42,"bar":None},catch_response=True)asresponse:
- try:
- ifresponsZ 4 A | - $ Ie.json()["greeting"]!="hello":
- response.failure("Didnotgetexpectedvalueingreeting")
- exceptJSONDecodeError:
- response.failure("Responsecouldnotbedecodd g |edasJSON")
- exceptKeyError:
- responsG 5 k v K $ Pe.failure("RespZ Y ) @ } c B UonsedidnotH \ 6 Vcontainexpectedkey'greeting'")
小结
loQ a U m M L y xcustfile是个普通Python模块,必须继承User类或其子类HttpUser等。本文对User类和Hc E ] E z 7 ittpUser类的属性和方法进行P c ; 3 { : M }了介u 0 { S )绍,使用它们可以编写性能测试的用户脚C $ 5 P d本。locustfile还有另外一个= # ; \ ] & ] }重要W * V 1 !组成元素,@P X 2 6 3 ~ _ gtask。