wrk is the HTTP benchmarking tool which combined with Lua scripts can be the bazooka in your benchmarking arsenal. This article shows an advanced example of using Lua scripts with wrk. I will also show you how to debug wrk.
First of all I would like to share the Docker awesomeness with you, so please make sure you have the following tools installed:
JSON File Example
The idea is very simple. We have an JSON file with request details and wrk will use this information in its benchmark. wrk doesn’t have this feature build in but we will use a Lua script to tell it what to do.
The JSON file might look like this:
[
{
“path”: “/path-1”,
“body”: “some content”,
“method”: “GET”,
“headers”: {
“X-Custom-Header-1”: “test 1”,
“X-Custom-Header-2”: “test 2”
}
},
{
“path”: “/path-2”,
“body”: “some content”,
“method”: “POST”,
“headers”: {
“X-Custom-Header-1”: “test 3”,
“X-Custom-Header-2”: “test 4”
}
}
]
As you can see each request has different properties.
Save it in the data directory as data/requests.json.
Now we need a proper lua script.
I prepared one below, save it as scripts/multi-request-json.lua:
— Module instantiation
local cjson = require “cjson”
local cjson2 = cjson.new()
local cjson_safe = require “cjson.safe”
— Initialize the pseudo random number generator
— Resource: http://lua-users.org/wiki/MathLibraryTutorial
math.randomseed(os.time())
math.random(); math.random(); math.random()
— Shuffle array
— Returns a randomly shuffled array
function shuffle(paths)
local j, k
local n = #paths
for i = 1, n do
j, k = math.random(n), math.random(n)
paths[j], paths[k] = paths[k], paths[j]
end
return paths
end
— Load URL paths from the file
function load_request_objects_from_file(file)
local data = {}
local content
— Check if the file exists
— Resource: http://stackoverflow.com/a/4991602/325852
local f=io.open(file,”r”)
if f~=nil then
content = f:read(“*all”)
io.close(f)
else
— Return the empty array
return lines
end
— Translate Lua value to/from JSON
data = cjson.decode(content)
return shuffle(data)
end
— Load URL requests from file
requests = load_request_objects_from_file(“/data/requests.json”)
— Check if at least one path was found in the file
if #requests <= 0 then
print(“multiplerequests: No requests found.”)
os.exit()
end
print(“multiplerequests: Found ” .. #requests .. ” requests”)
— Initialize the requests array iterator
counter = 1
request = function()
— Get the next requests array element
local request_object = requests
— Increment the counter
counter = counter + 1
— If the counter is longer than the requests array length then reset it
if counter > #requests then
counter = 1
end
— Return the request object with the current URL path
return wrk.format(request_object.method, request_object.path, request_object.headers, request_object.body)
end
This script is extended with a shuffle functionality and instead of loading URL paths line by line we laod a JSON file. Parsing JSON requires a JSON library, which is loaded at the very top. This library transforms the request details from a JSON string into a Lua array.
The only thing that stops us from testing this example is the missing JSON library itself – we need to install it.
Let’s summarise: you need to save the JSON file as data/requests.json and the Lua script as scripts/multi-request-json.lua.
We will run wrk in a Docker container. This diagram will give you a good overview of how this is done:
Run the benchmark with:
docker run –rm \
-v `pwd`/scripts:/scripts \
-v `pwd`/data:/data \
fg/wrk-json wrk -c1 -t1 -d5s -s /scripts/multi-request-json.lua http://$APPLICATION_IP:$APPLICATION_PORT
This command will pull the image from the public Docker registry. Then it executes the wrk command (inside the Docker container) which additionally takes the Lua script. The Lua script opens the /data/requests.json file, parses it and feeds wrk with the data. Everything is possible because we shared the appropriate direcotries with the Docker container by using the -v option.
The returned output might look like this:
multiplerequests: Found 2 requests
multiplerequests: Found 2 requests
Running 5s test @ http://10.135.232.163:3000
1 threads and 1 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 1.32ms 1.41ms 18.91ms 91.81%
Req/Sec 0.89k 304.69 1.47k 64.00%
4447 requests in 5.00s, 0.85MB read
Requests/sec: 888.67
Transfer/sec: 174.44KB
Adding weights to this example could be also very easy: just duplicate the request objects or adjust the Lua script to recognize a weight property.
I hope that you liked this wrk and Lua example.