mirror of
https://github.com/wg/wrk
synced 2026-06-10 00:55:51 +08:00
Compare commits
5 Commits
@@ -21,7 +21,7 @@ SRC := wrk.c net.c ssl.c aprintf.c stats.c script.c units.c \
|
||||
BIN := wrk
|
||||
|
||||
ODIR := obj
|
||||
OBJ := $(patsubst %.c,$(ODIR)/%.o,$(SRC))
|
||||
OBJ := $(patsubst %.c,$(ODIR)/%.o,$(SRC)) $(ODIR)/bytecode.o
|
||||
|
||||
LDIR = deps/luajit/src
|
||||
LIBS := -lluajit $(LIBS)
|
||||
@@ -34,16 +34,16 @@ clean:
|
||||
$(RM) $(BIN) obj/*
|
||||
@$(MAKE) -C deps/luajit clean
|
||||
|
||||
$(BIN): $(OBJ) $(ODIR)/bytecode.o
|
||||
$(BIN): $(OBJ)
|
||||
@echo LINK $(BIN)
|
||||
@$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
|
||||
|
||||
$(OBJ): config.h Makefile | $(ODIR)
|
||||
$(OBJ): config.h Makefile $(LDIR)/libluajit.a | $(ODIR)
|
||||
|
||||
$(ODIR): $(LDIR)/libluajit.a
|
||||
$(ODIR):
|
||||
@mkdir -p $@
|
||||
|
||||
$(ODIR)/bytecode.o: scripts/wrk.lua
|
||||
$(ODIR)/bytecode.o: src/wrk.lua
|
||||
@echo LUAJIT $<
|
||||
@$(SHELL) -c 'cd $(LDIR) && ./luajit -b $(CURDIR)/$< $(CURDIR)/$@'
|
||||
|
||||
@@ -52,7 +52,7 @@ $(ODIR)/%.o : %.c
|
||||
@$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
$(LDIR)/libluajit.a:
|
||||
@echo Building LuaJit...
|
||||
@echo Building LuaJIT...
|
||||
@$(MAKE) -C $(LDIR) BUILDMODE=static
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
@@ -5,7 +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.
|
||||
processing, and custom reporting. Several example scripts are located in
|
||||
scripts/
|
||||
|
||||
Basic Usage
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
-- example script that demonstrates response handling and
|
||||
-- retrieving an authentication token to set on all future
|
||||
-- requests
|
||||
|
||||
token = nil
|
||||
path = "/authenticate"
|
||||
|
||||
request = function()
|
||||
return wrk.format("GET", path)
|
||||
end
|
||||
|
||||
response = function(status, headers, body)
|
||||
if not token and status == 200 then
|
||||
token = headers["X-Token"]
|
||||
path = "/resource"
|
||||
wrk.headers["X-Token"] = token
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,14 @@
|
||||
-- example dynamic request script which demonstrates changing
|
||||
-- the request path and a header for each request
|
||||
-------------------------------------------------------------
|
||||
-- NOTE: each wrk thread has an independent Lua scripting
|
||||
-- context and thus there will be one counter per thread
|
||||
|
||||
counter = 0
|
||||
|
||||
request = function()
|
||||
path = "/" .. counter
|
||||
wrk.headers["X-Counter"] = counter
|
||||
counter = counter + 1
|
||||
return wrk.format(nil, path)
|
||||
end
|
||||
@@ -0,0 +1,16 @@
|
||||
-- example script demonstrating HTTP pipelining
|
||||
|
||||
init = function(args)
|
||||
wrk.init(args)
|
||||
|
||||
local r = {}
|
||||
r[1] = wrk.format(nil, "/?foo")
|
||||
r[2] = wrk.format(nil, "/?bar")
|
||||
r[3] = wrk.format(nil, "/?baz")
|
||||
|
||||
req = table.concat(r)
|
||||
end
|
||||
|
||||
request = function()
|
||||
return req
|
||||
end
|
||||
@@ -0,0 +1,6 @@
|
||||
-- example HTTP POST script which demonstrates setting the
|
||||
-- HTTP method, body, and adding a header
|
||||
|
||||
wrk.method = "POST"
|
||||
wrk.body = "foo=bar&baz=quux"
|
||||
wrk.headers["Content-Type"] = "application/x-www-form-urlencoded"
|
||||
@@ -0,0 +1,10 @@
|
||||
-- example reporting script which demonstrates a custom
|
||||
-- done() function that prints latency percentiles as CSV
|
||||
|
||||
done = function(summary, latency, requests)
|
||||
io.write("------------------------------\n")
|
||||
for _, p in pairs({ 50, 90, 99, 99.999 }) do
|
||||
n = latency:percentile(p)
|
||||
io.write(string.format("%g%%,%d\n", p, n))
|
||||
end
|
||||
end
|
||||
@@ -38,7 +38,7 @@ SSL_CTX *ssl_init() {
|
||||
CRYPTO_set_locking_callback(ssl_lock);
|
||||
CRYPTO_set_id_callback(ssl_id);
|
||||
|
||||
if ((ctx = SSL_CTX_new(TLSv1_client_method()))) {
|
||||
if ((ctx = SSL_CTX_new(SSLv23_client_method()))) {
|
||||
SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL);
|
||||
SSL_CTX_set_verify_depth(ctx, 0);
|
||||
SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY);
|
||||
|
||||
@@ -9,6 +9,7 @@ static struct config {
|
||||
uint64_t connections;
|
||||
uint64_t duration;
|
||||
uint64_t timeout;
|
||||
uint64_t pipeline;
|
||||
bool latency;
|
||||
bool dynamic;
|
||||
char *script;
|
||||
@@ -145,7 +146,7 @@ int main(int argc, char **argv) {
|
||||
script_init(t->L, cfg.script, argc - optind, &argv[optind]);
|
||||
|
||||
if (i == 0) {
|
||||
script_verify_request(t->L);
|
||||
cfg.pipeline = script_verify_request(t->L);
|
||||
cfg.dynamic = !script_is_static(t->L);
|
||||
if (script_want_response(t->L)) {
|
||||
parser_settings.on_header_field = header_field;
|
||||
@@ -330,6 +331,26 @@ static int calibrate(aeEventLoop *loop, long long id, void *data) {
|
||||
return AE_NOMORE;
|
||||
}
|
||||
|
||||
static int check_timeouts(aeEventLoop *loop, long long id, void *data) {
|
||||
thread *thread = data;
|
||||
connection *c = thread->cs;
|
||||
uint64_t now = time_us();
|
||||
|
||||
uint64_t maxAge = now - (cfg.timeout * 1000);
|
||||
|
||||
for (uint64_t i = 0; i < thread->connections; i++, c++) {
|
||||
if (maxAge > c->start) {
|
||||
thread->errors.timeout++;
|
||||
}
|
||||
}
|
||||
|
||||
if (stop || now >= thread->stop_at) {
|
||||
aeStop(loop);
|
||||
}
|
||||
|
||||
return TIMEOUT_INTERVAL_MS;
|
||||
}
|
||||
|
||||
static int sample_rate(aeEventLoop *loop, long long id, void *data) {
|
||||
thread *thread = data;
|
||||
|
||||
@@ -386,8 +407,6 @@ static int response_complete(http_parser *parser) {
|
||||
thread->complete++;
|
||||
thread->requests++;
|
||||
|
||||
stats_record(thread->latency, now - c->start);
|
||||
|
||||
if (status > 399) {
|
||||
thread->errors.status++;
|
||||
}
|
||||
@@ -403,40 +422,22 @@ static int response_complete(http_parser *parser) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!http_should_keep_alive(parser)) goto reconnect;
|
||||
if (--c->pending == 0) {
|
||||
stats_record(thread->latency, now - c->start);
|
||||
aeCreateFileEvent(thread->loop, c->fd, AE_WRITABLE, socket_writeable, c);
|
||||
}
|
||||
|
||||
if (!http_should_keep_alive(parser)) {
|
||||
reconnect_socket(thread, c);
|
||||
goto done;
|
||||
}
|
||||
|
||||
http_parser_init(parser, HTTP_RESPONSE);
|
||||
aeCreateFileEvent(thread->loop, c->fd, AE_WRITABLE, socket_writeable, c);
|
||||
|
||||
goto done;
|
||||
|
||||
reconnect:
|
||||
reconnect_socket(thread, c);
|
||||
|
||||
done:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int check_timeouts(aeEventLoop *loop, long long id, void *data) {
|
||||
thread *thread = data;
|
||||
connection *c = thread->cs;
|
||||
uint64_t now = time_us();
|
||||
|
||||
uint64_t maxAge = now - (cfg.timeout * 1000);
|
||||
|
||||
for (uint64_t i = 0; i < thread->connections; i++, c++) {
|
||||
if (maxAge > c->start) {
|
||||
thread->errors.timeout++;
|
||||
}
|
||||
}
|
||||
|
||||
if (stop || now >= thread->stop_at) {
|
||||
aeStop(loop);
|
||||
}
|
||||
|
||||
return TIMEOUT_INTERVAL_MS;
|
||||
}
|
||||
|
||||
static void socket_connected(aeEventLoop *loop, int fd, void *data, int mask) {
|
||||
connection *c = data;
|
||||
|
||||
@@ -449,8 +450,8 @@ static void socket_connected(aeEventLoop *loop, int fd, void *data, int mask) {
|
||||
http_parser_init(&c->parser, HTTP_RESPONSE);
|
||||
c->written = 0;
|
||||
|
||||
aeCreateFileEvent(c->thread->loop, c->fd, AE_READABLE, socket_readable, c);
|
||||
aeCreateFileEvent(c->thread->loop, c->fd, AE_WRITABLE, socket_writeable, c);
|
||||
aeCreateFileEvent(c->thread->loop, fd, AE_READABLE, socket_readable, c);
|
||||
aeCreateFileEvent(c->thread->loop, fd, AE_WRITABLE, socket_writeable, c);
|
||||
|
||||
return;
|
||||
|
||||
@@ -478,7 +479,10 @@ static void socket_writeable(aeEventLoop *loop, int fd, void *data, int mask) {
|
||||
case RETRY: return;
|
||||
}
|
||||
|
||||
if (!c->written) c->start = time_us();
|
||||
if (!c->written) {
|
||||
c->start = time_us();
|
||||
c->pending = cfg.pipeline;
|
||||
}
|
||||
|
||||
c->written += n;
|
||||
if (c->written == c->length) {
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
#include "script.h"
|
||||
#include "http_parser.h"
|
||||
|
||||
#define VERSION "3.0.3"
|
||||
#define VERSION "3.1.0"
|
||||
#define RECVBUF 8192
|
||||
#define SAMPLES 100000000
|
||||
|
||||
@@ -53,6 +53,7 @@ typedef struct connection {
|
||||
char *request;
|
||||
size_t length;
|
||||
size_t written;
|
||||
uint64_t pending;
|
||||
buffer headers;
|
||||
buffer body;
|
||||
char buf[RECVBUF];
|
||||
|
||||
Reference in New Issue
Block a user