wrk(2)- Lua 脚本的使用

本文涉及的产品
性能测试 PTS,5000VUM额度
简介: 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

 

运行结果

image.png

创建了 5 个线程, 以及每个线程发起的请求数和得到的响应数都有打印出来

 

工作上的模板栗子


Lua 脚本

为防止被盗,只放图片

image.png


image.png


官方脚本栗子


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

相关实践学习
通过性能测试PTS对云服务器ECS进行规格选择与性能压测
本文为您介绍如何利用性能测试PTS对云服务器ECS进行规格选择与性能压测。
相关文章
|
6月前
|
存储 NoSQL Redis
Redis的Lua脚本有什么作用?
Redis Lua脚本用于减少网络开销、实现原子操作及扩展指令集。它能合并操作降低网络延迟,保证原子性,替代不支持回滚的事务。通过脚本,代码复用率提高,且可自定义指令,如实现分布式锁,增强Redis功能和灵活性。
244 1
|
1月前
|
缓存 分布式计算 NoSQL
大数据-43 Redis 功能扩展 Lua 脚本 对Redis扩展 eval redis.call redis.pcall
大数据-43 Redis 功能扩展 Lua 脚本 对Redis扩展 eval redis.call redis.pcall
25 2
|
5月前
|
消息中间件 NoSQL Java
Redis系列学习文章分享---第六篇(Redis实战篇--Redis分布式锁+实现思路+误删问题+原子性+lua脚本+Redisson功能介绍+可重入锁+WatchDog机制+multiLock)
Redis系列学习文章分享---第六篇(Redis实战篇--Redis分布式锁+实现思路+误删问题+原子性+lua脚本+Redisson功能介绍+可重入锁+WatchDog机制+multiLock)
227 0
|
2月前
|
存储 JSON Ubuntu
如何使用 Lua 脚本进行更复杂的网络请求,比如 POST 请求?
如何使用 Lua 脚本进行更复杂的网络请求,比如 POST 请求?
|
3月前
|
存储 NoSQL Redis
Tair的发展问题之在Redis集群模式下,Lua脚本操作key面临什么问题,如何解决
Tair的发展问题之在Redis集群模式下,Lua脚本操作key面临什么问题,如何解决
|
6月前
|
缓存 NoSQL Java
【Redis】5、Redis 的分布式锁、Lua 脚本保证 Redis 命令的原子性
【Redis】5、Redis 的分布式锁、Lua 脚本保证 Redis 命令的原子性
226 0
|
6月前
|
算法 NoSQL Java
springboot整合redis及lua脚本实现接口限流
springboot整合redis及lua脚本实现接口限流
266 0
|
5月前
|
JSON 监控 数据格式
使用Lua代码扩展上网行为管理软件的脚本功能
本文介绍了如何使用Lua脚本增强上网行为管理,包括过滤URL、记录用户访问日志、控制带宽和自动提交监控数据到网站。Lua是一种轻量级语言,适合编写扩展脚本。文中提供多个示例代码,如URL过滤器、用户活动日志记录器和带宽控制器,帮助用户根据需求定制网络管理功能。通过这些示例,用户可以快速掌握Lua在上网行为管理中的应用。
171 4
|
5月前
|
NoSQL API Redis
使用Redis Lua脚本实现高级限流策略
使用Redis Lua脚本实现高级限流策略
198 0
|
5月前
|
消息中间件 NoSQL Java
Spring Boot中使用Redis和Lua脚本实现延时队列
Spring Boot中使用Redis和Lua脚本实现延时队列