Locustfile中的User類和HttpUser類
本文轉(zhuǎn)載自微信公眾號「dongfanger」,作者dongfanger。轉(zhuǎn)載本文請聯(lián)系dongfanger公眾號。
locustfile是什么?
locustfile是Locust性能測試工具的用戶腳本,描述了單個用戶的行為。
locustfile是個普通的Python模塊,如果寫作locustfile.py,那么路徑切換到文件所在目錄,直接執(zhí)行命令就能運行:
- $ locust
如果換個名字,那么只能通過-f參數(shù)指定文件名運行:
- $ locust -f locust_files/my_locust_file.py
與一般Python模塊不同的是:locustfile必須至少定義一個類,且繼承自User類。
User類
User類表示性能測試的模擬用戶,Locust會在運行時創(chuàng)建User類的實例。
wait_time屬性
設(shè)置等待時間,默認值不等待,立即執(zhí)行。
Locust支持4種方式設(shè)置wait_time屬性。
為了便于理解實際意義,我把源碼貼在了下面。
- constant函數(shù),常量,任務(wù)執(zhí)行完畢等待X秒開始下一任務(wù)。
- def constant(wait_time):
- """
- Returns a function that just returns the number specified by the wait_time argument
- Example::
- class MyUser(User):
- wait_time = constant(3)
- """
- return lambda instance: wait_time
- between函數(shù),區(qū)間隨機值,任務(wù)執(zhí)行完畢等待X-Y秒(中間隨機取值)開始下一任務(wù)。
- def between(min_wait, max_wait):
- """
- Returns a function that will return a random number between min_wait and max_wait.
- Example::
- class MyUser(User):
- # wait between 3.0 and 10.5 seconds after each task
- wait_time = between(3.0, 10.5)
- """
- return lambda instance: min_wait + random.random() * (max_wait - min_wait)
- constant_pacing函數(shù),自適應(yīng),若任務(wù)耗時超過該時間,則任務(wù)結(jié)束后立即執(zhí)行下一任務(wù);若任務(wù)耗時不超過該時間,則等待達到該時間后執(zhí)行下一任務(wù)。
- def constant_pacing(wait_time):
- """
- Returns a function that will track the run time of the tasks, and for each time it's
- called it will return a wait time that will try to make the total time between task
- execution equal to the time specified by the wait_time argument.
- In the following example the task will always be executed once every second, no matter
- the task execution time::
- class MyUser(User):
- wait_time = constant_pacing(1)
- @task
- def my_task(self):
- time.sleep(random.random())
- If a task execution exceeds the specified wait_time, the wait will be 0 before starting
- the next task.
- """
- def wait_time_func(self):
- if not hasattr(self, "_cp_last_run"):
- self._cp_last_wait_time = wait_time
- self._cp_last_run = time()
- return wait_time
- else:
- run_time = time() - self._cp_last_run - self._cp_last_wait_time
- self._cp_last_wait_time = max(0, wait_time - run_time)
- self._cp_last_run = time()
- return self._cp_last_wait_time
- return wait_time_func
- 自定義wait_time方法,比如每次等待時間1秒2秒3秒遞增:
- class MyUser(User):
- last_wait_time = 0
- def wait_time(self):
- self.last_wait_time += 1
- return self.last_wait_time
- ...
weight屬性
設(shè)置創(chuàng)建類實例的權(quán)重,默認每個類創(chuàng)建相同數(shù)量的實例。
locustfile中可以有多個繼承了User類的類。
命令行可以指定運行哪些類:
- $ locust -f locust_file.py WebUser MobileUser
通過weight屬性可以讓類更大概率創(chuàng)建實例,比如:
- class WebUser(User):
- weight = 3
- ...
- class MobileUser(User):
- weight = 1
- ...
WebUser類比MobileUser類多三倍概率創(chuàng)建實例。
host屬性
設(shè)置URL前綴。
一般是在Locust的Web UI或者命令行,通過--host指定URL前綴。如果沒有通過--host指定,并且類中設(shè)置了host屬性,那么類的host屬性才會生效。
environment屬性
對用戶運行環(huán)境的引用。
比如在task方法中通過environment屬性終止運行:
- self.environment.runner.quit()
注意,單機會終止所有運行,分布式只會終止單個worker節(jié)點。
on_start和on_stop方法
測試前初始化和測試后清理。
HttpUser類
開篇文章的示例腳本,沒有繼承User類,而是繼承了它的子類HttpUser:
它比User類更常用,因為它添加了一個client屬性,用來發(fā)送HTTP請求。
client屬性/HttpSession
HttpUser類的client屬性是HttpSession類的一個實例:
HttpSession是requests.Session的子類,requests就是常用來做接口測試的那個requests庫:
HttpSession沒有對requests.Session做什么改動,主要是傳遞請求結(jié)果給Locust,比如success/fail,response time,response length,name。
示例:
- response = self.client.post("/login", {"username":"testuser", "password":"secret"})
- print("Response status code:", response.status_code)
- print("Response text:", response.text)
- response = self.client.get("/my-profile")
由于requests.Session會暫存cookie,所以示例中登錄/login請求后可以繼續(xù)請求/my-profile。
斷言響應(yīng)結(jié)果
可以使用with語句和catch_response參數(shù)對響應(yīng)結(jié)果進行斷言:
- with self.client.get("/", catch_response=True) as response:
- if response.text == "Success":
- response.success()
- elif response.text != "Success":
- response.failure("Got wrong response")
- elif response.elapsed.total_seconds() > 0.5:
- response.failure("Request took too long")
或者直接拋出異常:
- from locust.exception import RescheduleTask
- ...
- with self.client.get("/does_not_exist/", catch_response=True) as response:
- if response.status_code == 404:
- raise RescheduleTask()
name參數(shù)
name參數(shù)用于把不同api按同一分組進行統(tǒng)計,比如:
- for i in range(10):
- self.client.get("/blog?id=%i" % i, name="/blog?id=[id]")
會按/blog/?id=[id]統(tǒng)計1條數(shù)據(jù),而不是分成10條數(shù)據(jù)。
HTTP代理
Locust默認設(shè)置了requests.Session的trust_env為False,不查找代理,以提高運行性能。如果需要可以設(shè)置locust_instance.client.trust_env為True。
示例代碼
請求REST API并斷言:
- from json import JSONDecodeError
- ...
- with self.client.post("/", json={"foo": 42, "bar": None}, catch_response=True) as response:
- try:
- if response.json()["greeting"] != "hello":
- response.failure("Did not get expected value in greeting")
- except JSONDecodeError:
- response.failure("Response could not be decoded as JSON")
- except KeyError:
- response.failure("Response did not contain expected key 'greeting'")
小結(jié)
locustfile是個普通Python模塊,必須繼承User類或其子類HttpUser等。本文對User類和HttpUser類的屬性和方法進行了介紹,使用它們可以編寫性能測試的用戶腳本。locustfile還有另外一個重要組成元素,@task。