Locust學(xué)習(xí)總結(jié)

簡(jiǎn)介

Locust是一個(gè)使用Python編寫(xiě)的可擴(kuò)展、分布式的開(kāi)源性能測(cè)試工具。

優(yōu)點(diǎn)

  • 相比于Jmeter、LoadRunner這種基于GUI的工具而言,Locust使用Python語(yǔ)言來(lái)描述測(cè)試場(chǎng)景使模擬用戶行為變得更加靈活和簡(jiǎn)潔,除了Http(s)協(xié)議之外,Locust可以測(cè)試任意協(xié)議的系統(tǒng),只需要實(shí)現(xiàn)Python調(diào)用對(duì)應(yīng)協(xié)議的庫(kù)進(jìn)行請(qǐng)求即可(類(lèi)似HttpLocust類(lèi))。
  • Locust的并發(fā)機(jī)制采用協(xié)程的方式,相比于進(jìn)程和線程減少了系統(tǒng)級(jí)資源調(diào)度,因此單機(jī)的產(chǎn)生的并發(fā)能力相比于LoadRunner、jmeter得到了大幅的提升。

安裝

pip install locust

Locust腳本編寫(xiě)

import queue
from locust import HttpLocust, TaskSet, task
from locust.clients import HttpSession
from sign import ParserData


class UserBehavior(TaskSet):

    parser_data = ParserData()  # 解析接口傳參類(lèi)
    _client = 20
    version = 119
    user_info = None

    @staticmethod
    def get_user_info(response):
        r = response.json().get('content')
        return {
            'market_id': r.get('marketId'),
            'token': r.get('token')
        }

    def on_start(self):
        try:
            user, password = self.locust.users.get_nowait()
        except queue.Empty:
            print('test data run out. test ended.')
            exit(0)
        client = HttpSession(base_url='http://login.xxxx.cn')
        data = self.parser_data(loginName=user,
                                password=password,
                                client=self._client,
                                version=self.version)
        response = client.post(url='/login', data=data)
        self.user_info = self.get_user_info(response)
        self.locust.users.put_nowait((user, password))

    @task(2)
    def index(self):
        url = '/index'
        data = self.parser_data(market_id=self.user_info['market_id'],
                                client=self._client,
                                version=self.version,
                                pnum='3')
        headers = {'Authorization': 'Barer:' + self.user_info['token'],
                   'Accept': 'application/vnd.hs-api.v1+json'}
        with self.client.post(url=url, data=data, headers=headers,
                              verify=False, catch_response=True) as response:
            if response.status_code == 200:
                response.success()
            else:
                response.failure('http error.')

    @task(1)
    def shop_car_list(self):
        url = '/shopCar/list'
        data = self.parser_data(market_id=self.user_info['market_id'],
                                ischaidan='1',
                                client=self._client,
                                version=self.version)
        headers = {'Authorization': 'Bearer:' + self.user_info['token'],
                   'Accept': 'application/vnd.hs-api.v1+json'}
        with self.client.post(name='ShopCar', url=url, data=data, headers=headers,
                              verify=False, catch_response=True) as response:
            if response.status_code != 200 or "失敗" in response.text:
                response.failure('response error.')
            else:
                response.success()


class Stay(TaskSet):

    index = 0

    def on_start(self):
        self.index += 1

    @task
    def get_error(self):
        response = self.client.get('/1', name='error', allow_redirects=False,
                                   verify=False, catch_response=True)
        if response.status_code == 200:
            response.success()
        else:
            response.failure('http error.')

    @task
    def logout(self):
        self.interrupt()


class User(TaskSet):

    tasks = {Stay: 1}

    @task(1)
    def user(self):
        self.client.get('/', verify=False)


class WebsiteUser(HttpLocust):
    task_set = UserBehavior
    host = 'https://xxxx.api.xxxx.cn'
    min_wait = 1000
    max_wait = 3000
    users = queue.Queue()
    users.put_nowait(('user1', '1232'))
    users.put_nowait(('user2', '1234'))
    users.put_nowait(('user3', '1321'))
    weight = 3
    stop_timeout = 20


class WebsiteU(HttpLocust):
    task_set = User
    host = 'https://www.baidu.com'
    min_wait = 0
    max_wait = 0
    weight = 1
    stop_timeout = 60

簡(jiǎn)單解釋下:

  • UserBehavior和WebsiteUser兩個(gè)類(lèi)實(shí)現(xiàn)測(cè)試場(chǎng)景使用3個(gè)用戶賬號(hào),每個(gè)用戶會(huì)去先登錄,然后分別去查看首頁(yè)和進(jìn)入購(gòu)物車(chē)頁(yè)面
  • 首先導(dǎo)入了需要用到的類(lèi),HttpLocust類(lèi)為L(zhǎng)ocust子類(lèi),模擬客戶端的請(qǐng)求類(lèi),Taskset類(lèi)為任務(wù)集類(lèi),task為任務(wù)裝飾器。

Taskset

  • UserBehavior為T(mén)askset子類(lèi),該類(lèi)主要用來(lái)定義每個(gè)虛擬用戶的操作行為

  • Taskset子類(lèi)中可以定義一個(gè)on_start方法在正式開(kāi)始測(cè)試前只執(zhí)行一次,相當(dāng)與初始化操作(這里說(shuō)的只執(zhí)行一次是每個(gè)虛擬用戶都會(huì)去執(zhí)行一次),比如獲取登錄token等操作。

  • teskset類(lèi)中的每個(gè)任務(wù)都需要用@task(weight=1)裝飾器去裝飾為一個(gè)任務(wù),weight為執(zhí)行的權(quán)重,如果不裝飾,locust不會(huì)認(rèn)為這是一個(gè)任務(wù),UserBehavior類(lèi)中@task(1)、@task(2)裝飾器表示3個(gè)用戶里面有2個(gè)用戶去模擬執(zhí)行index方法,有1個(gè)用戶去執(zhí)行shop_car_list方法

  • taskset類(lèi)中self.client屬性請(qǐng)求操作時(shí)傳入catch_response參數(shù),設(shè)置為T(mén)rue,可以標(biāo)記響應(yīng)結(jié)果為成功或失敗,即使響應(yīng)是成功的,也可以標(biāo)記為失敗,默認(rèn)為Fasle

  • taskset類(lèi)中self.client屬性請(qǐng)求操作時(shí)有一個(gè)name參數(shù),當(dāng)設(shè)置了name的值時(shí),最后的請(qǐng)求結(jié)果展示中name字段會(huì)顯示這里定義的name值,相當(dāng)與給這個(gè)方法起了一個(gè)別名.

  • taskset類(lèi)中interrupt(reschedule=True)方法在頂層的taskset類(lèi)(即被指定到Locust子類(lèi)中的taskset)中不可用,reschedule為T(mén)rue時(shí),從被嵌套的任務(wù)中出來(lái)立即執(zhí)行新的任務(wù),如果為False從被嵌套的任務(wù)中出來(lái)會(huì)等待min_time-max_time之間的隨機(jī)時(shí)間,然后再執(zhí)行新的任務(wù),這個(gè)方法主要用來(lái)跳出嵌套的任務(wù)集

HttpLocust

  • WebsiteUser為HttpLocust子類(lèi),該類(lèi)是用來(lái)模擬用戶的類(lèi),定義了一些用戶信息,及請(qǐng)求方式

  • HttpLocust子類(lèi)中task_set屬性用來(lái)指定模擬用戶執(zhí)行的操作,即Taskset子類(lèi)

  • HttpLocust子類(lèi)中的host屬性為被測(cè)試系統(tǒng)的host,當(dāng)命令行中沒(méi)有指定--host參數(shù)時(shí),此屬性會(huì)生效

  • HttpLocust子類(lèi)中的min_wait、max_weight為最大等待時(shí)間和最小等待時(shí)間,每個(gè)請(qǐng)求會(huì)從這兩個(gè)時(shí)間間隔中隨機(jī)取一個(gè)時(shí)間等待,相當(dāng)用戶實(shí)際操作系統(tǒng)時(shí)每個(gè)動(dòng)作的思考時(shí)間。若測(cè)試單個(gè)接口則對(duì)應(yīng)的值都設(shè)置為0即可。如果Taskset類(lèi)中定義了min_wait、max_weight則會(huì)覆蓋Locust子類(lèi)中定義的值。單位ms,默認(rèn)值1000ms

  • HttpLocust子類(lèi)中的stop_timeout屬性為執(zhí)行測(cè)試的時(shí)間,單位為s

  • HttpLocust子類(lèi)中的weight為該類(lèi)執(zhí)行的權(quán)重,當(dāng)有多個(gè)子類(lèi)時(shí)生效,如WebsiteUser、WebsiteU兩個(gè)HttpLocust子類(lèi)中weight值分別為3和1.

  • Locust默認(rèn)單機(jī)單進(jìn)程運(yùn)行,此模式下并不能充分利用單機(jī)的多處理器,可使用分布式運(yùn)行,即開(kāi)啟一個(gè)master,n個(gè)slave(n為處理器個(gè)數(shù)),master負(fù)責(zé)啟動(dòng)Locust的web服務(wù)和任務(wù)分發(fā),不會(huì)產(chǎn)生壓力,slave主要負(fù)責(zé)產(chǎn)生壓力

運(yùn)行模式:

no-web模式

no_web模式指在命令行中直接運(yùn)行

locust -f load_test.py -c 1 -r 1 -n 1
  • -f 指定要運(yùn)行的Locust性能測(cè)試文件
  • -c 指定模擬的并發(fā)用戶數(shù)
  • -r 指定每秒的啟動(dòng)用戶數(shù)
  • -n 指定運(yùn)行次數(shù)
  • -t 指定運(yùn)行的時(shí)間,例如300s,1m,1h
    寫(xiě)完腳本調(diào)試時(shí)可在該模式下運(yùn)行

單機(jī)單進(jìn)程運(yùn)行

locust -f load_test.py

分布式運(yùn)行

分布式運(yùn)行,有單機(jī)多進(jìn)程運(yùn)行和多機(jī)多進(jìn)程運(yùn)行兩種

locust -f load_test.py --master       master模式下啟動(dòng)locust
locust -f load_test.py --slave         啟動(dòng)一個(gè)locust slave節(jié)點(diǎn),單機(jī)多進(jìn)程模式
locust -f load_test.py --slave --master-host=192.168.105.11    啟動(dòng)一個(gè)locust slave節(jié)點(diǎn),多機(jī)模式下 
no_web模式下運(yùn)行
image.png
web模式
image.png
  • Number of users to simulate:需要模擬的虛擬用戶個(gè)數(shù)
  • Hatch rate (users spawned/second):?jiǎn)?dòng)虛擬用戶的速率,每秒產(chǎn)生出多少個(gè)用戶數(shù)


    QQ圖片20171109190334.png
  • 顯示并發(fā)數(shù)、響應(yīng)時(shí)間、異常率、每秒請(qǐng)求數(shù)等
  • reqs/sec(每秒請(qǐng)求數(shù))為根據(jù)最近2s請(qǐng)求數(shù)據(jù)計(jì)算得到的數(shù)據(jù),即瞬時(shí)值


    QQ圖片20171109190430.png
  • 顯示rps、響應(yīng)時(shí)間、并發(fā)數(shù)在整個(gè)測(cè)試運(yùn)行中的走勢(shì)圖


    QQ圖片20171109190455.png
  • 顯示測(cè)試過(guò)程中出現(xiàn)的所有失敗的請(qǐng)求

Exceptions顯示測(cè)試過(guò)程中出現(xiàn)的異常
Download Date提供測(cè)試結(jié)果csv文件的下載

測(cè)試數(shù)據(jù):

  • locust子類(lèi)中設(shè)置的數(shù)據(jù)是全局的,為list時(shí),使用自增方式取數(shù)據(jù),并發(fā)運(yùn)行時(shí)會(huì)出現(xiàn)取到的數(shù)據(jù)重復(fù)的情況,如果對(duì)數(shù)據(jù)唯一性有要求,使用python的queue隊(duì)列的數(shù)據(jù)結(jié)構(gòu)即可
  • taskset子類(lèi)中設(shè)置的數(shù)據(jù)是局部的,即每一個(gè)虛擬用戶都會(huì)有一個(gè)屬于自己的這個(gè)變量

queue數(shù)據(jù)結(jié)構(gòu)

隊(duì)列形式

q=queue.Queue(maxsize=3)       先進(jìn)先出隊(duì)列
q.put(1)      向隊(duì)列中存數(shù)據(jù)
q.get()        向隊(duì)列中取數(shù)據(jù)
q.put_nowait()       相當(dāng)于q.put(1, block=False),當(dāng)q隊(duì)列滿了之后put會(huì)觸發(fā)queue.Full異常
q.get_nowait()       相當(dāng)與q.get(block=False),當(dāng)q隊(duì)列為空之后get會(huì)觸發(fā)queue.Empty異常
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容