本文转载自微信公众号「dongfanger」,作者dongfanger。转载本文请联系dongfanger公众号。

locustfile是什么?

locustfile是Locust性能测试工具的用户脚本,描述了单个用户的行为。

locust` ] e Nfile是个普通的Python模块,f 4 v U如果写作locustfj J R Sile.py,那么路径切换到文件所在目录,直接执行命令就能运行:

  1. $locust

如果换个名字x c P [ . `,那么只能通过-f参数指定文件名运行:

  1. $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秒开始下一任务。
  1. defconstant(wait_td 2 \ X \ G a iime):
  2. """
  3. Returnsafunctionthatjustreturnsthenumberspecifiedbythewait_timeargument
  4. Example+ T = U::
  5. classMyUser(User):
  6. wait_time=constant(3)
  7. """
  8. returnlambdainstance:wait_time
  • between函数,区{ u 7 R \ V % C w间随机值,任务执行完毕等待X-Y秒(中间随机取值)开始下一任务。
  1. defbetween(min_wait,max_wait):
  2. """
  3. Returnsafunctionthatwillreturnarandomnumberbetween\ 2 0min_waitandmax_wait.
  4. Example::
  5. classMyUser(UseD n 0 5 9 m 0 Or):
  6. #waitbetween3.0and10.5secondsaftereachtask
  7. wai[ Z @ ^ L G Jt_tim: h 4 ) ,e=between(3.0,10.5)
  8. """
  9. 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务耗时超过该时间,则任务结束后立即执行下一任务;若任务耗时不超过该时间,则等待达到该时间后执行下一任务。
  1. defconstant_pacing(wait_time):
  2. """
  3. Returnsafunc) ; \ i i _ f - gtionthatwilltracktheruntimeofth% B R 6etasks,andforeachtimeit's
  4. calleditwill: k f w J Jreturnawaittimethatwillt# C 6 $rytomakethetotaltimebetweentask
  5. executionequaltothY N u ; *etimespecifiedbythe i u l - Y x O uewait_timeargument.
  6. Inthefollowingexamplethetaskwillalwaysbeexecutedon# k s .ceevery4 _ J N 1 ? V % /second,nomatter
  7. thet9 e Y 9 Vaskexecutiontime::
  8. clV o H d 6 L o T iassMyUser(User):
  9. wait_time=constant_pacingp q $(1)
  10. @task
  11. defmy_task(self):
  12. time.sA b @ & ? % 2 Wleep(random.random())
  13. Ifataskexecutionexceedsthespecifie; 0 = mdwait_time,thewaitwillbe0beforestarting
  14. thenexttask.
  15. """
  16. defwait_time_func(self):
  17. ifnothasattr(s5 + \ ? I I K Yelf,"_cp_lK 9 ! 5 # l M & kast_run"):
  18. self._cp_last_wait_time=waitv u Z W R t_time
  19. self._cp_last_run=time()
  20. returnwait_time
  21. else:
  22. run_time=timA D I q ) -e()-self._cp_last_run-self._cp_last_wait_time
  23. self._cp_last_i 1 ^ ] T - Mwait_tim; ( h P ue=max(0,wait_time-run_time)
  24. self._cp_last_run=time()
  25. returnself._cp_last_wait_time
  26. returnwait_time_func
  • 自定义wait_time方法,比如每次等待时间1秒2秒3秒递增:
  1. classMyUser(User):
  2. last_wait_time=0
  3. defwait_time(self):
  4. self.las- o u 7t_wait_time+=1
  5. returnself.last_wait_time
  6. ...

weight属性

设置创建类实例的权重,默认每个类创建相同数量的实例。

locustfile中可以有多个继承了User类的类。

命令行可以s H 5指定运行哪些类:

  1. $locust-flocust_file.pyWebUserMobileUser

通过weight属性可以x $ ^ )让类更大概率创建实例,比如:

  1. classWebUser(User):
  2. weight=3
  3. ...
  4. classMobileUser(User):
  5. weighi | C = . # B ) (t=1
  6. ...

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终止运行:

  1. 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。

示例:

  1. response=self.client.post("/login",{"username":"testuserS c G s y","password":") Y \ ysecret"})
  2. prinq o \t("Responsestatuscode:",response.sta~ o % # Gtus_c8 6 X $ ? 6 vode)
  3. print("Responsetext:",response.text)
  4. 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

  1. withself.client.get("/",catch_response=True)asresponse:
  2. ifresponse.text=="Success":
  3. rX 2 \ c Tesponse.success()
  4. elifresponse.text!="Success":
  5. response.failure("Gotwrongresponse")
  6. elifresponse.eli ~ S k 5 n 5apsed.total_seconds()>0.5( m o 6 x 4 ` P J:
  7. response.failure("Requesttooktoolong")

或者直接抛出异常:

  1. fromlocust.exceptionimpo4 x b G \ (rtRescheduleTask
  2. ...
  3. withself.client.get("/does_not_exist/",catch_response=True)asresponse:
  4. ifresponse.status_code==404:
  5. raiseRescheduleTask()

nI 9 \ J n ] Aame参数

name参数用于把不同apix o j L ` Q按同一分组进行统计,比如:

  1. foriinrange(10):
  2. 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

  1. fromjsonimportJSONDecodeError
  2. ...
  3. withself.client.post("/",json={4 ! 5"foo":42,"bar":None},catch_response=True)asresponse:
  4. try:
  5. ifresponsZ 4 A | - $ Ie.json()["greeting"]!="hello":
  6. response.failure("Didnotgetexpectedvalueingreeting")
  7. exceptJSONDecodeError:
  8. response.failure("Responsecouldnotbedecodd g |edasJSON")
  9. exceptKeyError:
  10. 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。