wrk入門(2):發送post請求


1 引言

       wrk的命令行參數只能發送類似於get、delete這種不在請求體中帶參數的http請求,如果要發送post這類請求,必須要寫Lua腳本。由於wrk具有內置的LuaJIT(用於Lua的即時編譯器),可以使用Lua腳本進行擴展。

       也不必擔心沒學過Lua不會寫腳本,Lua的可讀性很強,類似於Python。通過下面簡單的示例,完全可以模仿出一個更高級的腳本,況且官方也寫了好幾個模板供我們使用。

2 腳本結構

       在寫腳本之前,先研究一下腳本結構,該結構也反映了wrk的內部邏輯,有助於我們更好的理解和編寫腳本。如下圖:

       圖示為wrk的執行流程,分為以下幾個階段:

       1. 解析IP地址。

       2. 設置線程。

       3. 執行壓力測試,也被稱為運行階段。

       4. 完成測試。

       上圖只是一個單線程的圖示。如果是多線程,流程圖會變成這樣:

       此外,running(運行階段)還可以分為三個步驟:init(初始化),request(請求)和response(響應)。

       

       總結一下:wrk一次運行過程有1.resolve IP階段;2. setUp階段;3. running階段(分為init、request、reponse);4.done階段; 在上述的幾個階段中,都有對應的變量或方法提供調用。

3 不同階段的方法和變量

       這個小節就是介紹一下各個階段對應的方法和一個wrk全局表,大致過一遍眼熟一下就行,配合后面的腳本示例看簡直效果拔群。    

       wrk全局表,存着一些變量供后面的方法調用:

wrk = {
    scheme  = "http",
    host    = "localhost",
    port    = nil,
    method  = "GET",
    path    = "/",
    headers = {},
    body    = nil,
    thread  = <userdata>,
  }

       wrk.format方法:返回一個HTTP請求字符串,該字符串是傳遞的參數與wrk表中的值的合並。

function wrk.format(method, path, headers, body)

    wrk.format returns a HTTP request string containing the passed parameters
    merged with values from the wrk table.

       wrk.lookup方法:返回一個表,其中包含主機和服務對的所有已知地址。這對應於POSIX getaddrinfo()函數。

function wrk.lookup(host, service)

    wrk.lookup returns a table containing all known addresses for the host
    and service pair. This corresponds to the POSIX getaddrinfo() function.

       wrk.connect方法:如果可以連接到請求地址,則返回true,否則返回false。該地址必須是wrk.lookup方法返回的地址。

  function wrk.connect(addr)

    wrk.connect returns true if the address can be connected to, otherwise
    it returns false. The address must be one returned from wrk.lookup().

       以下全局變量是可選的,並且如果定義必須是函數,也對應着上面流程圖中的各個階段,下面將一個個介紹:

global setup    -- called during thread setup
global init     -- called when the thread is starting
global delay    -- called to get the request delay
global request  -- called to generate the HTTP request
global response -- called with HTTP response data
global done     -- called with results of run

        Setup階段:

        設置階段是ip地址已經被成功解析並且線程初始化完畢但是還未啟動的階段,用於將數據傳遞給線程。每個線程調用一次setup()並接收代表該線程的userdata對象。

        只能通過get()/ set()方法設置或獲取線程運行時需要的全局變量,變量值可以是布爾值,nil,數字和字符串值或表的值。

        thread:stop()只能在線程運階段調用。

function setup(thread)

  The setup phase begins after the target IP address has been resolved and all
  threads have been initialized but not yet started.

  setup() is called once for each thread and receives a userdata object
  representing the thread.

    thread.addr             - get or set the thread's server address
    thread:get(name)        - get the value of a global in the thread's env
    thread:set(name, value) - set the value of a global in the thread's env
    thread:stop()           - stop the thread

  Only boolean, nil, number, and string values or tables of the same may be
  transfered via get()/set() and thread:stop() can only be called while the
  thread is running.

        Running階段:

        運行階段從調用init()方法開始,之后循環調用request()和response()方法。

        init()每個線程初始化時調用。可以接受命令行參數,但是為了和wrk的命令行參數做區分,參數名以“--”開頭。比如:

wrk -c3 -d1s -t2 -s /scripts/debug.lua http://$APP1_PRIVATE_IP:3000 -- debug true

        delay()返回延遲發送下一個請求的毫秒數。

        request()需要為每個請求返回HTTP對象。在此函數中,我們可以修改method,headers,path,和 body。可以使用wrk.format()輔助函數來生成請求對象。比如:

return wrk.format(method, path, headers, body)

        response()在響應返回時調用。需要status(http響應狀態),headers(http響應頭),body(http響應體)三個參數,解析響應頭和響應體非常消耗計算機資源,因此,如果調用init()方法之后全局變量response是nil,wrk將不會解析響應頭和響應體。

Running

  function init(args)
  function delay()
  function request()
  function response(status, headers, body)

  The running phase begins with a single call to init(), followed by
  a call to request() and response() for each request cycle.

  The init() function receives any extra command line arguments for the
  script which must be separated from wrk arguments with "--".

  delay() returns the number of milliseconds to delay sending the next
  request.

  request() returns a string containing the HTTP request. Building a new
  request each time is expensive, when testing a high performance server
  one solution is to pre-generate all requests in init() and do a quick
  lookup in request().

  response() is called with the HTTP response status, headers, and body.
  Parsing the headers and body is expensive, so if the response global is
  nil after the call to init() wrk will ignore the headers and body.

        Done階段:

        當所有請求都已經完成並且請求結果已經被統計時調用。

        done()函數接收一個包含結果數據的表(表是lua內置數據類型),以及兩個統計對象:每個請求的延遲時間(以微秒為單位)、每個線程的請求速率(以每秒請求數為單位)。Done函數內,可以使用以下屬性:

function done(summary, latency, requests) The done() function receives a table containing result data, and two statistics objects representing the per-request latency and per-thread request rate. Duration and latency are microsecond values and rate is measured in requests per second.

latency.min
-- minimum value seen 延時最小值 latency.max -- maximum value seen 延時最大值 latency.mean -- average value seen 延時平均值 latency.stdev -- standard deviation 延時標准差 latency:percentile(99.0) -- 99th percentile value 第99%的值 latency[i] -- raw value and count 請求i的原始延遲數據 summary = { duration = N, -- run duration in microseconds 運行持續時間(以微秒為單位) requests = N, -- total completed requests 完成的請求總數 bytes = N, -- total bytes received 接受的總字節數 errors = { connect = N, -- total socket connection errors socket連接錯誤的總數目 read = N, -- total socket read errors socket讀取錯誤的總數目 write = N, -- total socket write errors socket寫入錯誤的總數目 status = N, -- total HTTP status codes > 399 HTTp狀態碼大於399的總數目 timeout = N -- total request timeouts 所有請求的超時時間總和 } }

4 Post請求示例

        首先,在wrk1(博主本地虛擬機)上創建腳本文件夾和文件

[root@iQOO-Z1 pyy]# mkdir scripts
[root@iQOO-Z1 pyy]# vi scripts/post.lua

        添加以下內容到post.lua:

wrk.method = "POST"
wrk.body = '{"username":"admin","password":"123456"}'
wrk.headers["Content-Type"] = "application/json"

response = function(status, headers, body)
print(body) --調試用,正式測試時需要關閉,因為解析response非常消耗資源
end

        這個腳本非常簡單,僅僅修改了全局wrk對象屬性和重載了response()方法,response()只是打印了一下響應體內容。使用帶有wrk的Lua腳本只需將文件路徑附加到-s參數后面即可:

# -v 數據卷掛載,將本地目錄與wrk容器內目錄共享; `pwd` 代表當前目錄
docker run --rm -v `pwd`/scripts:/scripts williamyeh/wrk -c1 -t1 -d1s -s /scripts/post.lua http://xxx.xx.xx.xx:port/url

        運行結果如下,可以看見已經登錄成功並且返回了用戶token:

        上面實現了post請求,但是還有提升效率的方法,比如每次測試都要輸入一遍命令未免有點繁瑣,所以可以考慮寫一個簡單的shell腳本來更快捷的進行測試。

        創建shell文件並輸入命令代碼,如下:

[root@iQOO-Z1 pyy]# vi login_test.sh

# content
docker run --rm -v `pwd`/scripts:/scripts williamyeh/wrk -c$1 -t$2 -d$3 -s /scripts/post.lua http://xx.x.xx.xx:port/login

[root@iQOO-Z1 pyy]# chmod +x login_test.sh
[root@iQOO-Z1 pyy]# ./login_test.sh 1 1 1s

        運行結果:

        查看更多腳本模板(可能需要FQ) 

        Lua語法練習     

        未完待續........


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM