wrk(2)- Lua 腳本的使用


背景

  • 要用 wrk 進行壓測
  • 看了下其他同事的壓測,都用了 Lua 腳本來自定義一些東西
  • 所以這一篇主要講 Lua 腳本

 

Lua 介紹

  • Lua 腳本是一種輕量小巧的腳本語言,用標准 c 語言編寫,並以源代碼形式開放
  • 其設計目的是為了嵌入應用程序中,從而為程序提供靈活的擴展和定制功能。
  • wrk 工具嵌入了 Lua 腳本語言
  • 因此,在自定義壓測場景時,可在 wrk 目錄下使用 Lua 定制壓測場景

 

Lua 腳本的三個階段

wrk 支持在三個不同的階段執行 LuaJIT 腳本

  • setup:設置階段
  • running:運行階段
  • done:結束階段

每個 wrk 線程都有一個獨立的腳本環境,因為獨有獨立的 Lua 虛擬機

setup、done 階段在一個單獨的環境中執行,不參與 running 階段

官方文檔:https://github.com/wg/wrk/blob/master/SCRIPTING

 

POST 請求

前言

  • 之前說過,如果沒有自定義的 Lua 腳本,wrk 默認發送的是 HTTP 1.1 GET 請求
  • 這里如果想發起 POST 請求的話,Lua 腳本要怎么寫

 

官方腳本

-- POST 請求,演示如何添加
-- HTTP method, body, header

wrk.method = "POST"
wrk.body   = "foo=bar&baz=quux"
wrk.headers["Content-Type"] = "application/x-www-form-urlencoded"

  

wrk 變量

  • wrk 是一個內置的全局 table 類型變量,不需要定義可以直接使用
  • 修改 wrk 變量的值,會對所有請求都生效
wrk = {
    scheme  = "http",
    host    = "localhost",
    port    = nil,
    method  = "GET",
    path    = "/",
    headers = {},
    body    = nil,
    thread  = <userdata>
}

  

wrk 內置函數

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

  • 根據函數的參數和全局 wrk 變量,返回一個自定義的 http 請求字符串
  • 注意:函數的參數會覆蓋 wrk 全局變量對應的參數值
  • 可以通過 format 可以構造出不同的 request

 

function wrk.lookup(host, service)

返回所有可用服務器的地址信息

 

function wrk.connect(addr)

  • 測試指定的服務器地址是否能正常連接
  • 如果地址可以連接到 wrk.connect,則返回true,否則返回false
  • 地址必須是從 wrk.lookup() 返回的地址

 

Lua 腳本三個階段的內置函數

前言

上面也說到有三個階段,setup、running、done 階段,他們分別都有一些內置函數

 

setup 啟動階段

function setup(thread)

  • 每個線程初始化時執行一次,wrk 會在測試線程已經初始化但還沒有啟動的時候調用該方法
  • setup 方法會傳入一個 thread 對象,可以修改或設置 thread 相關參數,也可以終止線程執行
  • 這里一般做一些初始化的工作,例如讀取配置文件,加載到內存(不要每次請求的時候讀取一遍,這樣對測試准確性影響很大)

 

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,終止線程
  • 只有布爾值、nil值、數字和字符串值或相同的 table 可以通過 get() / set() 進行操作
  • thread:stop() 只能在線程運行時被調用

 

running 運行階段

function init(args)

  • 由線程調用,在線程開始啟動時僅執行一次
  • args 是通過命令行傳入的參數,通過 -- 指定

 

function delay()

  • 每次發送請求時,間隔時間(ms)
  • 每次發送請求前都會執行一次

 

function request()

  • 每次發送請求都會執行一次
  • 返回一個自定義的 HTTP 請求字符串

 

官方建議

  • 每次構建一個新的請求都很耗時耗資源
  • 當測試高性能服務器時,建議在 init() 中預生成所有請求,並在 request() 中進行快速查找

 

實際使用

  • 一般在這里會配合 wrk.format() 方法,動態創建請求
  • 這里不要執行耗時的代碼,否則會影響測試結果准確性

 

function response(status, headers, body)

  • 每次請求得到響應時執行一次
  • status:響應狀態碼
  • headers:響應頭
  • body:響應體
  • 解析 header 和 body 的開銷比較大,所以如果沒有定義 response 回調方法的話,wrk 就不會解析 header 和 body
  • 這樣測試結果會更加准確(解析響應數據是客戶端負責的,不能算到服務器處理時間里面)

 

done 結束階段

function done(summary, latency, requests)

  • 返回最終測試結果時執行,整個測試過程只執行一次
  • 可以生成自定義測試報告,但如果沒有特別需求就沒必要重寫了
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
latency(i)               -- raw value and count

summary = {
  duration = N,  -- run duration in microseconds
  requests = N,  -- total completed requests
  bytes    = N,  -- total bytes received
  errors   = {
    connect = N, -- total socket connection errors
    read    = N, -- total socket read errors
    write   = N, -- total socket write errors
    status  = N, -- total HTTP status codes > 399
    timeout = N  -- total request timeouts
  }
}

這個感覺不常用,用到再舉栗子吧

 

具體的栗子

Lua 腳本

-- example script that demonstrates use of setup() to pass
-- data to and from the threads

local counter = 1
local threads = {}

function setup(thread)
-- 給每個線程設置一個 id 參數
   thread:set("id", counter)
-- 將線程添加到 table 中
   table.insert(threads, thread)
   counter = counter + 1
end

function init(args)
-- 初始化兩個參數,每個線程都有獨立的 requests、responses 參數
   requests  = 0
   responses = 0

-- 打印線程被創建的消息,打印完后,線程正式啟動運行
   local msg = "thread %d created"
   print(msg:format(id))
end

function request()
-- 每發起一次請求 +1
   requests = requests + 1
   return wrk.request()
end

function response(status, headers, body)
-- 每得到一次請求的響應 +1
   responses = responses + 1
end

function done(summary, latency, requests)
-- 循環線程 table
   for index, thread in ipairs(threads) do
      local id        = thread:get("id")
      local requests  = thread:get("requests")
      local responses = thread:get("responses")
      local msg = "thread %d made %d requests and got %d responses"
-- 打印每個線程發起了多少個請求,得到了多少次響應
      print(msg:format(id, requests, responses))
   end
end

 

運行命令

wrk -d3s -c20 -t5 -s test.lua https://*****/get

 

運行結果

創建了 5 個線程, 以及每個線程發起的請求數和得到的響應數都有打印出來 

 

工作上的模板栗子

Lua 腳本

為防止被盜,只放圖片

 

官方腳本栗子

https://github.com/wg/wrk/tree/master/scripts


免責聲明!

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



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