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
wrk(2)- Lua 脚本的使用的更多相关文章
- wrk中的lua脚本(转)
		
转载地址:http://www.tuicool.com/articles/IFjIJjU wrk是一款现代化的http压测工具,提供lua脚本的功能可以满足每个请求或部分请求的差异化. wrk中执行h ...
 - 在redis中使用lua脚本让你的灵活性提高5个逼格
		
在redis的官网上洋洋洒洒的大概提供了200多个命令,貌似看起来很多,但是这些都是别人预先给你定义好的,但你却不能按照自己的意图进行定制, 所以是不是感觉自己还是有一种被束缚的感觉,有这个感觉就对了 ...
 - 《转》Unity3D研究院编辑器之创建Lua脚本模板
		
Unity里能创建 c#脚本模板,但是如果我想创建Lua脚本模板怎么办呢?拓展一下编辑器吧. 设置一下Lua脚本的模板地址 : Assets/Editor/Lua/Template/lua.lua ...
 - StackExchange.Redis加载Lua脚本进行模糊查询的批量删除和修改
		
前言 使用StackExchange.Redis没有直接相关的方法进行模糊查询的批量删除和修改操作,虽然可以通过Scan相关的方法进行模糊查询,例如:HashScan("hashkey&qu ...
 - 使用Lua脚本语言开发出高扩展性的系统,AgileEAS.NET SOA中间件Lua脚本引擎介绍
		
一.前言 AgileEAS.NET SOA 中间件平台是一款基于基于敏捷并行开发思想和Microsoft .Net构件(组件)开发技术而构建的一个快速开发应用平台.用于帮助中小型软件企业建立一条适合市 ...
 - redisTemplate的spring配置以及lua脚本驱动
		
最近在使用spring-data-redis的redisTemplate,所以写篇使用记录吧. 1.不用多说,使用maven引入相关依赖,因为项目已经引入其他的 <dependency> ...
 - redis原子性读写操作之LUA脚本和watch机制
		
最近在开发电商平台的子系统--储值卡系统,系统核心业务涉及到金额消费以及库存控制,因此为了解决建立在内存上高并发情况下的事务控制,使用了spring封装的RedisTemplate执行lua脚本进行原 ...
 - online_jf.lua --累计在线时间领取物品(积分)的lua脚本
		
原作者: ayase 8-27修正 修复首次使用后的红字不需要额外进数据库导入计分表,这lua全自动生成 ----------------------------------------------- ...
 - Redis执行Lua脚本的情况
		
第一个测试: 往Redis里面存入1000个Hash,每个Hash里面有100个元素(Key 0-99,值是Key^2). PHP代码,执行33s左右 <?php $redis = new Re ...
 
随机推荐
- SSRF(服务端请求伪造)漏洞
			
目录 SSRF SSRF漏洞的挖掘 SSRF漏洞利用 SSRF漏洞防御 SSRF SSRF(Server-Side Request Forgery,服务器端请求伪造)漏洞,是一种由攻击者构造请求,由服 ...
 - 使用C#操作注册表
			
这节讲一下使用C#操作注册表. 首先来了解一下,什么是注册表,注册表是Windows中特有的一个东西,百度百科中对其解释如下:Windows注册表(Registry)实质上是一个庞大的数据库,它存储着 ...
 - 记一次 .NET 车联网云端服务 CPU爆高分析
			
一:背景 1. 讲故事 前几天有位朋友wx求助,它的程序CPU经常飙满,没找到原因,希望帮忙看一下. 这些天连续接到几个cpu爆高的dump,都看烦了,希望后面再来几个其他方面的dump,从沟通上看, ...
 - 面试 CSS篇清除浮动及display:inline-block
			
一. 在div使用display:inline-block时,HTML代码中的回车换行键会被转化为一个空白符 如图 <style type="text/css"> *{ ...
 - Java_集合之一
			
1.Collection集合 1.1数组和集合的区别[理解] 相同点 都是容器,可以存储多个数据 不同点 数组的长度是不可变的,集合的长度是可变的 数组可以存基本数据类型和引用数据类型 集合只能存引用 ...
 - 如何利用CRM系统打通营销全渠道?
			
企业经常通过不同渠道组织各种形式的营销推广,可惜,这些营销推广的效果往往差强人意. 相关研究表明,很多营销推广不理想的主要原因是不同营销渠道之间没有打通数据,不清楚每个营销渠道或营销策划的投入产出.推 ...
 - CRM系统实施中如何避免“踩雷”
			
CRM系统实施后,效果没有达到预期是很多企业出现的情况.而最主要的原因就是在实施之前没有足够的思考和相应的计划.那么,企业应如何避免CRM系统实施时"踩雷"?这里有几条建议希望可以 ...
 - memcache 和 redis 的区别
			
1)Redis中,并不是所有的数据都一直存储在内存中的,这是和Memcache相比一个最大的区别.2)Redis在很多方面具备数据库的特征,或者说就是一个数据库系统,而Memcache只是简单的K/V ...
 - spring-第三章-jdbc
			
一,回顾 aop:面向切面编程,就是将一些和主业务流程没有关系的公共代码,提取封装到切面类,通过切入点规则,可以对目标方法进行功能增强;也就是可以再目标方法执行的前后添加一段额外逻辑代码; 二,Jdb ...
 - 佳能m62套机5500 佳能EOS M50 M6 MARK2 II二代 最低到过5800
			
佳能m62套机5500 佳能EOS M50 M6 MARK2 II二代