mirror of
https://github.com/wg/wrk
synced 2025-02-09 11:32:52 +08:00
add script setup() and thread methods
This commit is contained in:
parent
6f0aa32ede
commit
9b84d3e1a4
6
CHANGES
6
CHANGES
@ -1,4 +1,8 @@
|
||||
master
|
||||
wrk next
|
||||
|
||||
* The wrk global variable is the only global defined by default.
|
||||
* wrk.init() calls the global init(), remove calls to wrk.init().
|
||||
* Add wrk.lookup(host, port) and wrk.connect(addr) functions.
|
||||
* Add setup phase that calls the global setup() for each thread.
|
||||
* Allow assignment to thread.addr to specify the server address.
|
||||
* Add thread:set(key, value), thread:get(key), and thread:stop().
|
||||
|
1
Makefile
1
Makefile
@ -9,6 +9,7 @@ ifeq ($(TARGET), sunos)
|
||||
else ifeq ($(TARGET), darwin)
|
||||
LDFLAGS += -pagezero_size 10000 -image_base 100000000
|
||||
else ifeq ($(TARGET), linux)
|
||||
CFLAGS += -D_POSIX_C_SOURCE=200112L -D_BSD_SOURCE
|
||||
LIBS += -ldl
|
||||
LDFLAGS += -Wl,-E
|
||||
else ifeq ($(TARGET), freebsd)
|
||||
|
71
README
71
README
@ -5,8 +5,8 @@ wrk - a HTTP benchmarking tool
|
||||
design with scalable event notification systems such as epoll and kqueue.
|
||||
|
||||
An optional LuaJIT script can perform HTTP request generation, response
|
||||
processing, and custom reporting. Several example scripts are located in
|
||||
scripts/
|
||||
processing, and custom reporting. Details are available in SCRIPTING and
|
||||
several examples are located in scripts/
|
||||
|
||||
Basic Usage
|
||||
|
||||
@ -26,65 +26,6 @@ Basic Usage
|
||||
Requests/sec: 748868.53
|
||||
Transfer/sec: 606.33MB
|
||||
|
||||
Scripting
|
||||
|
||||
wrk's public Lua API is:
|
||||
|
||||
init = function(args)
|
||||
request = function()
|
||||
response = function(status, headers, body)
|
||||
done = function(summary, latency, requests)
|
||||
|
||||
wrk = {
|
||||
scheme = "http",
|
||||
host = "localhost",
|
||||
port = nil,
|
||||
method = "GET",
|
||||
path = "/",
|
||||
headers = {},
|
||||
body = nil
|
||||
}
|
||||
|
||||
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.
|
||||
|
||||
The following globals are optional, and if defined must be functions:
|
||||
|
||||
global init -- called when the thread is initialized
|
||||
global request -- returning the HTTP message for each request
|
||||
global response -- called with HTTP response data
|
||||
global done -- called with results of run
|
||||
|
||||
The init() function receives any extra command line arguments for the
|
||||
script which must be separated from wrk arguments with "--".
|
||||
|
||||
The done() function receives a table containing result data, and two
|
||||
statistics objects representing the sampled 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
|
||||
latency[i] -- raw sample value
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
Benchmarking Tips
|
||||
|
||||
The machine running wrk must have a sufficient number of ephemeral ports
|
||||
@ -93,11 +34,9 @@ Benchmarking Tips
|
||||
than the number of concurrent connections being tested.
|
||||
|
||||
A user script that only changes the HTTP method, path, adds headers or
|
||||
a body, will have no performance impact. If multiple HTTP requests are
|
||||
necessary they should be pre-generated and returned via a quick lookup in
|
||||
the request() call. Per-request actions, particularly building a new HTTP
|
||||
request, and use of response() will necessarily reduce the amount of load
|
||||
that can be generated.
|
||||
a body, will have no performance impact. Per-request actions, particularly
|
||||
building a new HTTP request, and use of response() will necessarily reduce
|
||||
the amount of load that can be generated.
|
||||
|
||||
Acknowledgements
|
||||
|
||||
|
112
SCRIPTING
Normal file
112
SCRIPTING
Normal file
@ -0,0 +1,112 @@
|
||||
Overview
|
||||
|
||||
wrk supports executing a LuaJIT script during three distinct phases: setup,
|
||||
running, and done. Each wrk thread has an independent scripting environment
|
||||
and the setup & done phases execute in a separate environment which does
|
||||
not participate in the running phase.
|
||||
|
||||
The public Lua API consists of a global table and a number of global
|
||||
functions:
|
||||
|
||||
wrk = {
|
||||
scheme = "http",
|
||||
host = "localhost",
|
||||
port = nil,
|
||||
method = "GET",
|
||||
path = "/",
|
||||
headers = {},
|
||||
body = nil,
|
||||
thread = <userdata>,
|
||||
}
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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().
|
||||
|
||||
The following globals are optional, and if defined must be functions:
|
||||
|
||||
global setup -- called during thread setup
|
||||
global init -- called when the thread is starting
|
||||
global request -- called to generate the HTTP request
|
||||
global response -- called with HTTP response data
|
||||
global done -- called with results of run
|
||||
|
||||
Setup
|
||||
|
||||
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(key) - get the value of a global in the thread's env
|
||||
thread:set(key, 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
|
||||
|
||||
function init(args)
|
||||
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 "--".
|
||||
|
||||
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
|
||||
|
||||
function done(summary, latency, requests)
|
||||
|
||||
The done() function receives a table containing result data, and two
|
||||
statistics objects representing the sampled 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
|
||||
latency[i] -- raw sample value
|
||||
|
||||
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
|
||||
}
|
||||
}
|
22
scripts/addr.lua
Normal file
22
scripts/addr.lua
Normal file
@ -0,0 +1,22 @@
|
||||
-- example script that demonstrates use of setup() to pass
|
||||
-- a random server address to each thread
|
||||
|
||||
local addrs = nil
|
||||
|
||||
function setup(thread)
|
||||
if not addrs then
|
||||
addrs = wrk.lookup(wrk.host, wrk.port or "http")
|
||||
for i = #addrs, 1, -1 do
|
||||
if not wrk.connect(addrs[i]) then
|
||||
table.remove(addrs, i)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
thread.addr = addrs[math.random(#addrs)]
|
||||
end
|
||||
|
||||
function init(args)
|
||||
local msg = "thread addr: %s"
|
||||
print(msg:format(wrk.thread.addr))
|
||||
end
|
38
scripts/setup.lua
Normal file
38
scripts/setup.lua
Normal file
@ -0,0 +1,38 @@
|
||||
-- example script that demonstrates use of setup() to pass
|
||||
-- data to and from the threads
|
||||
|
||||
local counter = 1
|
||||
local threads = {}
|
||||
|
||||
function setup(thread)
|
||||
thread:set("id", counter)
|
||||
table.insert(threads, thread)
|
||||
counter = counter + 1
|
||||
end
|
||||
|
||||
function init(args)
|
||||
requests = 0
|
||||
responses = 0
|
||||
|
||||
local msg = "thread %d created"
|
||||
print(msg:format(id))
|
||||
end
|
||||
|
||||
function request()
|
||||
requests = requests + 1
|
||||
return wrk.request()
|
||||
end
|
||||
|
||||
function response(status, headers, body)
|
||||
responses = responses + 1
|
||||
end
|
||||
|
||||
function done(summary, latency, requests)
|
||||
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
|
10
scripts/stop.lua
Normal file
10
scripts/stop.lua
Normal file
@ -0,0 +1,10 @@
|
||||
-- example script that demonstrates use of thread:stop()
|
||||
|
||||
local counter = 1
|
||||
|
||||
function response()
|
||||
if counter == 100 then
|
||||
wrk.thread:stop()
|
||||
end
|
||||
counter = counter + 1
|
||||
end
|
@ -5,7 +5,6 @@
|
||||
#define HAVE_KQUEUE
|
||||
#elif defined(__linux__)
|
||||
#define HAVE_EPOLL
|
||||
#define _POSIX_C_SOURCE 200809L
|
||||
#elif defined (__sun)
|
||||
#define HAVE_EVPORT
|
||||
#endif
|
||||
|
@ -16,7 +16,6 @@
|
||||
#include <signal.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/uio.h>
|
||||
|
||||
|
205
src/script.c
205
src/script.c
@ -15,35 +15,43 @@ typedef struct {
|
||||
static int script_addr_tostring(lua_State *);
|
||||
static int script_addr_gc(lua_State *);
|
||||
static int script_stats_len(lua_State *);
|
||||
static int script_stats_get(lua_State *);
|
||||
static int script_stats_index(lua_State *);
|
||||
static int script_thread_index(lua_State *);
|
||||
static int script_thread_newindex(lua_State *);
|
||||
static int script_wrk_lookup(lua_State *);
|
||||
static int script_wrk_connect(lua_State *);
|
||||
|
||||
static void set_fields(lua_State *, int index, const table_field *);
|
||||
|
||||
static const struct luaL_reg addrlib[] = {
|
||||
{ "__tostring", script_addr_tostring },
|
||||
{ "__gc" , script_addr_gc },
|
||||
{ NULL, NULL }
|
||||
{ "__tostring", script_addr_tostring },
|
||||
{ "__gc" , script_addr_gc },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static const struct luaL_reg statslib[] = {
|
||||
{ "__index", script_stats_get },
|
||||
{ "__len", script_stats_len },
|
||||
{ NULL, NULL }
|
||||
{ "__index", script_stats_index },
|
||||
{ "__len", script_stats_len },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
lua_State *script_create(char *scheme, char *host, char *port, char *path) {
|
||||
static const struct luaL_reg threadlib[] = {
|
||||
{ "__index", script_thread_index },
|
||||
{ "__newindex", script_thread_newindex },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
lua_State *script_create(char *file, char *scheme, char *host, char *port, char *path) {
|
||||
lua_State *L = luaL_newstate();
|
||||
luaL_openlibs(L);
|
||||
luaL_dostring(L, "wrk = require \"wrk\"");
|
||||
(void) luaL_dostring(L, "wrk = require \"wrk\"");
|
||||
|
||||
luaL_newmetatable(L, "wrk.addr");
|
||||
luaL_register(L, NULL, addrlib);
|
||||
lua_pop(L, 1);
|
||||
|
||||
luaL_newmetatable(L, "wrk.stats");
|
||||
luaL_register(L, NULL, statslib);
|
||||
lua_pop(L, 1);
|
||||
luaL_newmetatable(L, "wrk.thread");
|
||||
luaL_register(L, NULL, threadlib);
|
||||
|
||||
const table_field fields[] = {
|
||||
{ "scheme", LUA_TSTRING, scheme },
|
||||
@ -56,19 +64,17 @@ lua_State *script_create(char *scheme, char *host, char *port, char *path) {
|
||||
};
|
||||
|
||||
lua_getglobal(L, "wrk");
|
||||
set_fields(L, 1, fields);
|
||||
lua_pop(L, 1);
|
||||
set_fields(L, 4, fields);
|
||||
lua_pop(L, 4);
|
||||
|
||||
if (file && luaL_dofile(L, file)) {
|
||||
const char *cause = lua_tostring(L, -1);
|
||||
fprintf(stderr, "%s: %s\n", file, cause);
|
||||
}
|
||||
|
||||
return L;
|
||||
}
|
||||
|
||||
void script_prepare_setup(lua_State *L, char *script) {
|
||||
if (script && luaL_dofile(L, script)) {
|
||||
const char *cause = lua_tostring(L, -1);
|
||||
fprintf(stderr, "%s: %s\n", script, cause);
|
||||
}
|
||||
}
|
||||
|
||||
bool script_resolve(lua_State *L, char *host, char *service) {
|
||||
lua_getglobal(L, "wrk");
|
||||
|
||||
@ -83,13 +89,24 @@ bool script_resolve(lua_State *L, char *host, char *service) {
|
||||
return count > 0;
|
||||
}
|
||||
|
||||
struct addrinfo *script_peek_addr(lua_State *L) {
|
||||
void script_push_thread(lua_State *L, thread *t) {
|
||||
thread **ptr = (thread **) lua_newuserdata(L, sizeof(thread **));
|
||||
*ptr = t;
|
||||
luaL_getmetatable(L, "wrk.thread");
|
||||
lua_setmetatable(L, -2);
|
||||
}
|
||||
|
||||
void script_setup(lua_State *L, thread *t) {
|
||||
lua_getglobal(t->L, "wrk");
|
||||
script_push_thread(t->L, t);
|
||||
lua_setfield(t->L, -2, "thread");
|
||||
lua_pop(t->L, 1);
|
||||
|
||||
lua_getglobal(L, "wrk");
|
||||
lua_getfield(L, -1, "addrs");
|
||||
lua_rawgeti(L, -1, 1);
|
||||
struct addrinfo *addr = lua_touserdata(L, -1);
|
||||
lua_pop(L, 3);
|
||||
return addr;
|
||||
lua_getfield(L, -1, "setup");
|
||||
script_push_thread(L, t);
|
||||
lua_call(L, 1, 0);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
void script_headers(lua_State *L, char **headers) {
|
||||
@ -106,12 +123,7 @@ void script_headers(lua_State *L, char **headers) {
|
||||
lua_pop(L, 2);
|
||||
}
|
||||
|
||||
void script_init(lua_State *L, char *script, int argc, char **argv) {
|
||||
if (script && luaL_dofile(L, script)) {
|
||||
const char *cause = lua_tostring(L, -1);
|
||||
fprintf(stderr, "%s: %s\n", script, cause);
|
||||
}
|
||||
|
||||
void script_init(lua_State *L, int argc, char **argv) {
|
||||
lua_getglobal(L, "wrk");
|
||||
lua_getfield(L, -1, "init");
|
||||
lua_newtable(L);
|
||||
@ -211,21 +223,19 @@ void script_errors(lua_State *L, errors *errors) {
|
||||
lua_setfield(L, 1, "errors");
|
||||
}
|
||||
|
||||
void script_done(lua_State *L, stats *latency, stats *requests) {
|
||||
stats **s;
|
||||
void script_push_stats(lua_State *L, stats *s) {
|
||||
stats **ptr = (stats **) lua_newuserdata(L, sizeof(stats **));
|
||||
*ptr = s;
|
||||
luaL_getmetatable(L, "wrk.stats");
|
||||
lua_setmetatable(L, -2);
|
||||
}
|
||||
|
||||
void script_done(lua_State *L, stats *latency, stats *requests) {
|
||||
lua_getglobal(L, "done");
|
||||
lua_pushvalue(L, 1);
|
||||
|
||||
s = (stats **) lua_newuserdata(L, sizeof(stats **));
|
||||
*s = latency;
|
||||
luaL_getmetatable(L, "wrk.stats");
|
||||
lua_setmetatable(L, 4);
|
||||
|
||||
s = (stats **) lua_newuserdata(L, sizeof(stats **));
|
||||
*s = requests;
|
||||
luaL_getmetatable(L, "wrk.stats");
|
||||
lua_setmetatable(L, 5);
|
||||
script_push_stats(L, latency);
|
||||
script_push_stats(L, requests);
|
||||
|
||||
lua_call(L, 3, 0);
|
||||
lua_pop(L, 1);
|
||||
@ -278,6 +288,20 @@ static struct addrinfo *checkaddr(lua_State *L) {
|
||||
return addr;
|
||||
}
|
||||
|
||||
void script_addr_copy(struct addrinfo *src, struct addrinfo *dst) {
|
||||
*dst = *src;
|
||||
dst->ai_addr = zmalloc(src->ai_addrlen);
|
||||
memcpy(dst->ai_addr, src->ai_addr, src->ai_addrlen);
|
||||
}
|
||||
|
||||
struct addrinfo *script_addr_clone(lua_State *L, struct addrinfo *addr) {
|
||||
struct addrinfo *udata = lua_newuserdata(L, sizeof(*udata));
|
||||
luaL_getmetatable(L, "wrk.addr");
|
||||
lua_setmetatable(L, -2);
|
||||
script_addr_copy(addr, udata);
|
||||
return udata;
|
||||
}
|
||||
|
||||
static int script_addr_tostring(lua_State *L) {
|
||||
struct addrinfo *addr = checkaddr(L);
|
||||
char host[NI_MAXHOST];
|
||||
@ -313,7 +337,7 @@ static int script_stats_percentile(lua_State *L) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int script_stats_get(lua_State *L) {
|
||||
static int script_stats_index(lua_State *L) {
|
||||
stats *s = checkstats(L);
|
||||
if (lua_isnumber(L, 2)) {
|
||||
int index = luaL_checkint(L, 2);
|
||||
@ -337,6 +361,59 @@ static int script_stats_len(lua_State *L) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static thread *checkthread(lua_State *L) {
|
||||
thread **t = luaL_checkudata(L, 1, "wrk.thread");
|
||||
luaL_argcheck(L, t != NULL, 1, "`thread' expected");
|
||||
return *t;
|
||||
}
|
||||
|
||||
static int script_thread_get(lua_State *L) {
|
||||
thread *t = checkthread(L);
|
||||
const char *key = lua_tostring(L, -1);
|
||||
lua_getglobal(t->L, key);
|
||||
script_copy_value(t->L, L, -1);
|
||||
lua_pop(t->L, 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int script_thread_set(lua_State *L) {
|
||||
thread *t = checkthread(L);
|
||||
const char *key = lua_tostring(L, -2);
|
||||
script_copy_value(L, t->L, -1);
|
||||
lua_setglobal(t->L, key);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int script_thread_stop(lua_State *L) {
|
||||
thread *t = checkthread(L);
|
||||
aeStop(t->loop);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int script_thread_index(lua_State *L) {
|
||||
thread *t = checkthread(L);
|
||||
const char *key = lua_tostring(L, 2);
|
||||
if (!strcmp("get", key)) lua_pushcfunction(L, script_thread_get);
|
||||
if (!strcmp("set", key)) lua_pushcfunction(L, script_thread_set);
|
||||
if (!strcmp("stop", key)) lua_pushcfunction(L, script_thread_stop);
|
||||
if (!strcmp("addr", key)) script_addr_clone(L, t->addr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int script_thread_newindex(lua_State *L) {
|
||||
thread *t = checkthread(L);
|
||||
const char *key = lua_tostring(L, -2);
|
||||
if (!strcmp("addr", key)) {
|
||||
struct addrinfo *addr = checkaddr(L);
|
||||
if (t->addr) zfree(t->addr->ai_addr);
|
||||
t->addr = zrealloc(t->addr, sizeof(*addr));
|
||||
script_addr_copy(addr, t->addr);
|
||||
} else {
|
||||
luaL_error(L, "cannot set '%s' on thread", luaL_typename(L, -1));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int script_wrk_lookup(lua_State *L) {
|
||||
struct addrinfo *addrs;
|
||||
struct addrinfo hints = {
|
||||
@ -356,13 +433,7 @@ static int script_wrk_lookup(lua_State *L) {
|
||||
|
||||
lua_newtable(L);
|
||||
for (struct addrinfo *addr = addrs; addr != NULL; addr = addr->ai_next) {
|
||||
struct addrinfo *udata = lua_newuserdata(L, sizeof(*udata));
|
||||
luaL_getmetatable(L, "wrk.addr");
|
||||
lua_setmetatable(L, -2);
|
||||
|
||||
*udata = *addr;
|
||||
udata->ai_addr = zmalloc(addr->ai_addrlen);
|
||||
memcpy(udata->ai_addr, addr->ai_addr, addr->ai_addrlen);
|
||||
script_addr_clone(L, addr);
|
||||
lua_rawseti(L, -2, index++);
|
||||
}
|
||||
|
||||
@ -381,6 +452,36 @@ static int script_wrk_connect(lua_State *L) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
void script_copy_value(lua_State *src, lua_State *dst, int index) {
|
||||
switch (lua_type(src, index)) {
|
||||
case LUA_TBOOLEAN:
|
||||
lua_pushboolean(dst, lua_toboolean(src, index));
|
||||
break;
|
||||
case LUA_TNIL:
|
||||
lua_pushnil(dst);
|
||||
break;
|
||||
case LUA_TNUMBER:
|
||||
lua_pushnumber(dst, lua_tonumber(src, index));
|
||||
break;
|
||||
case LUA_TSTRING:
|
||||
lua_pushstring(dst, lua_tostring(src, index));
|
||||
break;
|
||||
case LUA_TTABLE:
|
||||
lua_newtable(dst);
|
||||
lua_pushnil(src);
|
||||
while (lua_next(src, index - 1)) {
|
||||
script_copy_value(src, dst, -1);
|
||||
script_copy_value(src, dst, -2);
|
||||
lua_settable(dst, -3);
|
||||
lua_pop(src, 1);
|
||||
}
|
||||
lua_pop(src, 1);
|
||||
break;
|
||||
default:
|
||||
luaL_error(src, "cannot transfer '%s' to thread", luaL_typename(src, index));
|
||||
}
|
||||
}
|
||||
|
||||
static void set_fields(lua_State *L, int index, const table_field *fields) {
|
||||
for (int i = 0; fields[i].name; i++) {
|
||||
table_field f = fields[i];
|
||||
|
23
src/script.h
23
src/script.h
@ -5,28 +5,21 @@
|
||||
#include <lua.h>
|
||||
#include <lualib.h>
|
||||
#include <lauxlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <netdb.h>
|
||||
#include <unistd.h>
|
||||
#include "stats.h"
|
||||
#include "wrk.h"
|
||||
|
||||
typedef struct {
|
||||
char *buffer;
|
||||
size_t length;
|
||||
char *cursor;
|
||||
} buffer;
|
||||
lua_State *script_create(char *, char *, char *, char *, char *);
|
||||
|
||||
lua_State *script_create(char *, char *, char *, char *);
|
||||
void script_prepare_setup(lua_State *, char *);
|
||||
bool script_resolve(lua_State *, char *, char *);
|
||||
struct addrinfo *script_peek_addr(lua_State *);
|
||||
void script_headers(lua_State *, char **);
|
||||
size_t script_verify_request(lua_State *L);
|
||||
|
||||
void script_init(lua_State *, char *, int, char **);
|
||||
void script_setup(lua_State *, thread *);
|
||||
void script_done(lua_State *, stats *, stats *);
|
||||
|
||||
void script_headers(lua_State *, char **);
|
||||
void script_init(lua_State *, int, char **);
|
||||
void script_request(lua_State *, char **, size_t *);
|
||||
void script_response(lua_State *, int, buffer *, buffer *);
|
||||
size_t script_verify_request(lua_State *L);
|
||||
|
||||
bool script_is_static(lua_State *);
|
||||
bool script_want_response(lua_State *L);
|
||||
@ -34,6 +27,8 @@ bool script_has_done(lua_State *L);
|
||||
void script_summary(lua_State *, uint64_t, uint64_t, uint64_t);
|
||||
void script_errors(lua_State *, errors *);
|
||||
|
||||
void script_copy_value(lua_State *, lua_State *, int);
|
||||
|
||||
void buffer_append(buffer *, const char *, size_t);
|
||||
void buffer_reset(buffer *);
|
||||
char *buffer_pushlstring(lua_State *, char *);
|
||||
|
30
src/wrk.c
30
src/wrk.c
@ -1,6 +1,7 @@
|
||||
// Copyright (C) 2012 - Will Glozer. All rights reserved.
|
||||
|
||||
#include "wrk.h"
|
||||
#include "script.h"
|
||||
#include "main.h"
|
||||
|
||||
static struct config {
|
||||
@ -82,14 +83,6 @@ int main(int argc, char **argv) {
|
||||
path = &url[parser_url.field_data[UF_PATH].off];
|
||||
}
|
||||
|
||||
lua_State *L = script_create(schema, host, port, path);
|
||||
script_prepare_setup(L, cfg.script);
|
||||
if (!script_resolve(L, host, service)) {
|
||||
char *msg = strerror(errno);
|
||||
fprintf(stderr, "unable to connect to %s:%s %s\n", host, service, msg);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!strncmp("https", schema, 5)) {
|
||||
if ((cfg.ctx = ssl_init()) == NULL) {
|
||||
fprintf(stderr, "unable to initialize SSL\n");
|
||||
@ -111,19 +104,25 @@ int main(int argc, char **argv) {
|
||||
statistics.requests = stats_alloc(SAMPLES);
|
||||
|
||||
thread *threads = zcalloc(cfg.threads * sizeof(thread));
|
||||
uint64_t connections = cfg.connections / cfg.threads;
|
||||
uint64_t stop_at = time_us() + (cfg.duration * 1000000);
|
||||
uint64_t stop_at = time_us() + (cfg.duration * 1000000);
|
||||
|
||||
lua_State *L = script_create(cfg.script, schema, host, port, path);
|
||||
if (!script_resolve(L, host, service)) {
|
||||
char *msg = strerror(errno);
|
||||
fprintf(stderr, "unable to connect to %s:%s %s\n", host, service, msg);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for (uint64_t i = 0; i < cfg.threads; i++) {
|
||||
thread *t = &threads[i];
|
||||
thread *t = &threads[i];
|
||||
t->loop = aeCreateEventLoop(10 + cfg.connections * 3);
|
||||
t->addr = script_peek_addr(L);
|
||||
t->connections = connections;
|
||||
t->connections = cfg.connections / cfg.threads;
|
||||
t->stop_at = stop_at;
|
||||
|
||||
t->L = script_create(schema, host, port, path);
|
||||
t->L = script_create(cfg.script, schema, host, port, path);
|
||||
script_headers(t->L, headers);
|
||||
script_init(t->L, cfg.script, argc - optind, &argv[optind]);
|
||||
script_setup(L, t);
|
||||
script_init(t->L, argc - optind, &argv[optind]);
|
||||
|
||||
if (i == 0) {
|
||||
cfg.pipeline = script_verify_request(t->L);
|
||||
@ -474,7 +473,6 @@ static void socket_writeable(aeEventLoop *loop, int fd, void *data, int mask) {
|
||||
reconnect_socket(thread, c);
|
||||
}
|
||||
|
||||
|
||||
static void socket_readable(aeEventLoop *loop, int fd, void *data, int mask) {
|
||||
connection *c = data;
|
||||
size_t n;
|
||||
|
@ -6,13 +6,14 @@
|
||||
#include <inttypes.h>
|
||||
#include <sys/types.h>
|
||||
#include <netdb.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/err.h>
|
||||
#include <lua.h>
|
||||
|
||||
#include "stats.h"
|
||||
#include "ae.h"
|
||||
#include "script.h"
|
||||
#include "http_parser.h"
|
||||
|
||||
#define VERSION "3.1.2"
|
||||
@ -43,6 +44,12 @@ typedef struct {
|
||||
struct connection *cs;
|
||||
} thread;
|
||||
|
||||
typedef struct {
|
||||
char *buffer;
|
||||
size_t length;
|
||||
char *cursor;
|
||||
} buffer;
|
||||
|
||||
typedef struct connection {
|
||||
thread *thread;
|
||||
http_parser parser;
|
||||
|
10
src/wrk.lua
10
src/wrk.lua
@ -5,7 +5,8 @@ local wrk = {
|
||||
method = "GET",
|
||||
path = "/",
|
||||
headers = {},
|
||||
body = nil
|
||||
body = nil,
|
||||
thread = nil,
|
||||
}
|
||||
|
||||
function wrk.resolve(host, service)
|
||||
@ -18,6 +19,13 @@ function wrk.resolve(host, service)
|
||||
wrk.addrs = addrs
|
||||
end
|
||||
|
||||
function wrk.setup(thread)
|
||||
thread.addr = wrk.addrs[1]
|
||||
if type(setup) == "function" then
|
||||
setup(thread)
|
||||
end
|
||||
end
|
||||
|
||||
function wrk.init(args)
|
||||
if not wrk.headers["Host"] then
|
||||
local host = wrk.host
|
||||
|
Loading…
Reference in New Issue
Block a user