h2load adding timeout options
This commit is contained in:
parent
71623b674e
commit
727ba4906c
|
@ -19,6 +19,9 @@ requests
|
||||||
This is the subset of the number reported in ``failed`` and most
|
This is the subset of the number reported in ``failed`` and most
|
||||||
likely the network level failures or stream was reset by
|
likely the network level failures or stream was reset by
|
||||||
RST_STREAM.
|
RST_STREAM.
|
||||||
|
timeout
|
||||||
|
The number of requests whose connection timed out before they
|
||||||
|
were completed.
|
||||||
|
|
||||||
status codes
|
status codes
|
||||||
The number of status code h2load received.
|
The number of status code h2load received.
|
||||||
|
|
121
src/h2load.cc
121
src/h2load.cc
|
@ -74,8 +74,9 @@ namespace h2load {
|
||||||
Config::Config()
|
Config::Config()
|
||||||
: data_length(-1), addrs(nullptr), nreqs(1), nclients(1), nthreads(1),
|
: data_length(-1), addrs(nullptr), nreqs(1), nclients(1), nthreads(1),
|
||||||
max_concurrent_streams(-1), window_bits(30), connection_window_bits(30),
|
max_concurrent_streams(-1), window_bits(30), connection_window_bits(30),
|
||||||
rate(0), nconns(0), no_tls_proto(PROTO_HTTP2), data_fd(-1), port(0),
|
rate(0), nconns(0), conn_active_timeout(0), conn_inactivity_timeout(0),
|
||||||
default_port(0), verbose(false) {}
|
no_tls_proto(PROTO_HTTP2), data_fd(-1), port(0), default_port(0),
|
||||||
|
verbose(false) {}
|
||||||
|
|
||||||
Config::~Config() {
|
Config::~Config() {
|
||||||
freeaddrinfo(addrs);
|
freeaddrinfo(addrs);
|
||||||
|
@ -118,14 +119,16 @@ RequestStat::RequestStat() : data_offset(0), completed(false) {}
|
||||||
|
|
||||||
Stats::Stats(size_t req_todo)
|
Stats::Stats(size_t req_todo)
|
||||||
: req_todo(0), req_started(0), req_done(0), req_success(0),
|
: req_todo(0), req_started(0), req_done(0), req_success(0),
|
||||||
req_status_success(0), req_failed(0), req_error(0), bytes_total(0),
|
req_status_success(0), req_failed(0), req_error(0), req_timedout(0),
|
||||||
bytes_head(0), bytes_body(0), status(), req_stats(req_todo) {}
|
bytes_total(0), bytes_head(0), bytes_body(0), status(),
|
||||||
|
req_stats(req_todo) {}
|
||||||
|
|
||||||
Stream::Stream() : status_success(-1) {}
|
Stream::Stream() : status_success(-1) {}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
void writecb(struct ev_loop *loop, ev_io *w, int revents) {
|
void writecb(struct ev_loop *loop, ev_io *w, int revents) {
|
||||||
auto client = static_cast<Client *>(w->data);
|
auto client = static_cast<Client *>(w->data);
|
||||||
|
client->restart_timeout();
|
||||||
auto rv = client->do_write();
|
auto rv = client->do_write();
|
||||||
if (rv == Client::ERR_CONNECT_FAIL) {
|
if (rv == Client::ERR_CONNECT_FAIL) {
|
||||||
client->disconnect();
|
client->disconnect();
|
||||||
|
@ -145,6 +148,7 @@ void writecb(struct ev_loop *loop, ev_io *w, int revents) {
|
||||||
namespace {
|
namespace {
|
||||||
void readcb(struct ev_loop *loop, ev_io *w, int revents) {
|
void readcb(struct ev_loop *loop, ev_io *w, int revents) {
|
||||||
auto client = static_cast<Client *>(w->data);
|
auto client = static_cast<Client *>(w->data);
|
||||||
|
client->restart_timeout();
|
||||||
if (client->do_read() != 0) {
|
if (client->do_read() != 0) {
|
||||||
client->fail();
|
client->fail();
|
||||||
return;
|
return;
|
||||||
|
@ -178,6 +182,22 @@ void second_timeout_w_cb(struct ev_loop *loop, ev_timer *w, int revents) {
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
// Called when an a connection has been inactive for a set period of time
|
||||||
|
// or a fixed amount of time after all requests have been made on a
|
||||||
|
// connection
|
||||||
|
void conn_timeout_cb(EV_P_ ev_timer *w, int revents) {
|
||||||
|
auto client = static_cast<Client *>(w->data);
|
||||||
|
|
||||||
|
ev_timer_stop(client->worker->loop, &client->conn_inactivity_watcher);
|
||||||
|
ev_timer_stop(client->worker->loop, &client->conn_active_watcher);
|
||||||
|
|
||||||
|
if (util::check_socket_connected(client->fd)) {
|
||||||
|
client->timeout();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
Client::Client(Worker *worker, size_t req_todo)
|
Client::Client(Worker *worker, size_t req_todo)
|
||||||
: worker(worker), ssl(nullptr), next_addr(config.addrs), reqidx(0),
|
: worker(worker), ssl(nullptr), next_addr(config.addrs), reqidx(0),
|
||||||
state(CLIENT_IDLE), first_byte_received(false), req_todo(req_todo),
|
state(CLIENT_IDLE), first_byte_received(false), req_todo(req_todo),
|
||||||
|
@ -187,6 +207,14 @@ Client::Client(Worker *worker, size_t req_todo)
|
||||||
|
|
||||||
wev.data = this;
|
wev.data = this;
|
||||||
rev.data = this;
|
rev.data = this;
|
||||||
|
|
||||||
|
conn_inactivity_watcher.data = this;
|
||||||
|
ev_init(&conn_inactivity_watcher, conn_timeout_cb);
|
||||||
|
conn_inactivity_watcher.repeat = worker->config->conn_inactivity_timeout;
|
||||||
|
|
||||||
|
conn_active_watcher.data = this;
|
||||||
|
ev_timer_init(&conn_active_watcher, conn_timeout_cb,
|
||||||
|
worker->config->conn_active_timeout, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
Client::~Client() { disconnect(); }
|
Client::~Client() { disconnect(); }
|
||||||
|
@ -197,6 +225,10 @@ int Client::do_write() { return writefn(*this); }
|
||||||
int Client::connect() {
|
int Client::connect() {
|
||||||
record_start_time(&worker->stats);
|
record_start_time(&worker->stats);
|
||||||
|
|
||||||
|
if (worker->config->conn_inactivity_timeout > 0) {
|
||||||
|
ev_timer_again(worker->loop, &conn_inactivity_watcher);
|
||||||
|
}
|
||||||
|
|
||||||
while (next_addr) {
|
while (next_addr) {
|
||||||
auto addr = next_addr;
|
auto addr = next_addr;
|
||||||
next_addr = next_addr->ai_next;
|
next_addr = next_addr->ai_next;
|
||||||
|
@ -244,6 +276,18 @@ int Client::connect() {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Client::timeout() {
|
||||||
|
process_timedout_streams();
|
||||||
|
|
||||||
|
disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::restart_timeout() {
|
||||||
|
if (worker->config->conn_inactivity_timeout > 0) {
|
||||||
|
ev_timer_again(worker->loop, &conn_inactivity_watcher);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Client::fail() {
|
void Client::fail() {
|
||||||
process_abandoned_streams();
|
process_abandoned_streams();
|
||||||
|
|
||||||
|
@ -251,6 +295,9 @@ void Client::fail() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::disconnect() {
|
void Client::disconnect() {
|
||||||
|
ev_timer_stop(worker->loop, &conn_inactivity_watcher);
|
||||||
|
ev_timer_stop(worker->loop, &conn_active_watcher);
|
||||||
|
|
||||||
streams.clear();
|
streams.clear();
|
||||||
session.reset();
|
session.reset();
|
||||||
state = CLIENT_IDLE;
|
state = CLIENT_IDLE;
|
||||||
|
@ -274,6 +321,25 @@ void Client::submit_request() {
|
||||||
auto req_stat = &worker->stats.req_stats[worker->stats.req_started++];
|
auto req_stat = &worker->stats.req_stats[worker->stats.req_started++];
|
||||||
session->submit_request(req_stat);
|
session->submit_request(req_stat);
|
||||||
++req_started;
|
++req_started;
|
||||||
|
|
||||||
|
// if an active timeout is set and this is the last request to be submitted
|
||||||
|
// on this connection, start the active timeout.
|
||||||
|
if (worker->config->conn_active_timeout > 0 && req_started >= req_todo) {
|
||||||
|
ev_timer_start(worker->loop, &conn_active_watcher);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::process_timedout_streams() {
|
||||||
|
for (auto &req_stat : worker->stats.req_stats) {
|
||||||
|
if (!req_stat.completed) {
|
||||||
|
req_stat.stream_close_time = std::chrono::steady_clock::now();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto req_timed_out = req_todo - req_done;
|
||||||
|
worker->stats.req_timedout += req_timed_out;
|
||||||
|
|
||||||
|
process_abandoned_streams();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::process_abandoned_streams() {
|
void Client::process_abandoned_streams() {
|
||||||
|
@ -1078,7 +1144,8 @@ Options:
|
||||||
Available protocol: )";
|
Available protocol: )";
|
||||||
#endif // !HAVE_SPDYLAY
|
#endif // !HAVE_SPDYLAY
|
||||||
out << NGHTTP2_CLEARTEXT_PROTO_VERSION_ID << R"(
|
out << NGHTTP2_CLEARTEXT_PROTO_VERSION_ID << R"(
|
||||||
Default: )" << NGHTTP2_CLEARTEXT_PROTO_VERSION_ID << R"(
|
Default: )"
|
||||||
|
<< NGHTTP2_CLEARTEXT_PROTO_VERSION_ID << R"(
|
||||||
-d, --data=<PATH>
|
-d, --data=<PATH>
|
||||||
Post FILE to server. The request method is changed to
|
Post FILE to server. The request method is changed to
|
||||||
POST.
|
POST.
|
||||||
|
@ -1100,10 +1167,26 @@ Options:
|
||||||
to make the -n requests specified. The default value
|
to make the -n requests specified. The default value
|
||||||
for this option is 0. The -n option is not required if
|
for this option is 0. The -n option is not required if
|
||||||
the -C option is being used.
|
the -C option is being used.
|
||||||
|
-T, --connection-active-timeout=<N>
|
||||||
|
Specifies the maximum time that h2load is willing to
|
||||||
|
keep a connection open, regardless of the activity on
|
||||||
|
said connection. <N> must be a positive integer,
|
||||||
|
specifying the number of seconds to wait. When no
|
||||||
|
timeout value is set (either active or inactive), h2load
|
||||||
|
will keep a connection open indefinitely, waiting for a
|
||||||
|
response.
|
||||||
|
-N, --connection-inactivity-timeout=<N>
|
||||||
|
Specifies the amount of time that h2load is willing to
|
||||||
|
wait to see activity on a given connection. <N> must be
|
||||||
|
a positive integer, specifying the number of seconds to
|
||||||
|
wait. When no timeout value is set (either active or
|
||||||
|
inactive), h2load will keep a connection open
|
||||||
|
indefinitely, waiting for a response.
|
||||||
-v, --verbose
|
-v, --verbose
|
||||||
Output debug information.
|
Output debug information.
|
||||||
--version Display version information and exit.
|
--version Display version information and exit.
|
||||||
-h, --help Display this help and exit.)" << std::endl;
|
-h, --help Display this help and exit.)"
|
||||||
|
<< std::endl;
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
@ -1137,10 +1220,12 @@ int main(int argc, char **argv) {
|
||||||
{"ciphers", required_argument, &flag, 2},
|
{"ciphers", required_argument, &flag, 2},
|
||||||
{"rate", required_argument, nullptr, 'r'},
|
{"rate", required_argument, nullptr, 'r'},
|
||||||
{"num-conns", required_argument, nullptr, 'C'},
|
{"num-conns", required_argument, nullptr, 'C'},
|
||||||
|
{"connection-active-timeout", required_argument, nullptr, 'T'},
|
||||||
|
{"connection-inactivity-timeout", required_argument, nullptr, 'N'},
|
||||||
{nullptr, 0, nullptr, 0}};
|
{nullptr, 0, nullptr, 0}};
|
||||||
int option_index = 0;
|
int option_index = 0;
|
||||||
auto c = getopt_long(argc, argv, "hvW:c:d:m:n:p:t:w:H:i:r:C:", long_options,
|
auto c = getopt_long(argc, argv, "hvW:c:d:m:n:p:t:w:H:i:r:C:T:N:",
|
||||||
&option_index);
|
long_options, &option_index);
|
||||||
if (c == -1) {
|
if (c == -1) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1251,6 +1336,22 @@ int main(int argc, char **argv) {
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case 'T':
|
||||||
|
config.conn_active_timeout = strtoul(optarg, nullptr, 10);
|
||||||
|
if (config.conn_active_timeout <= 0) {
|
||||||
|
std::cerr << "-T: the conn_active_timeout wait time "
|
||||||
|
<< "must be positive." << std::endl;
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'N':
|
||||||
|
config.conn_inactivity_timeout = strtoul(optarg, nullptr, 10);
|
||||||
|
if (config.conn_inactivity_timeout <= 0) {
|
||||||
|
std::cerr << "-N: the conn_inactivity_timeout wait time "
|
||||||
|
<< "must be positive." << std::endl;
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
break;
|
||||||
case 'v':
|
case 'v':
|
||||||
config.verbose = true;
|
config.verbose = true;
|
||||||
break;
|
break;
|
||||||
|
@ -1656,6 +1757,7 @@ int main(int argc, char **argv) {
|
||||||
stats.req_todo += s.req_todo;
|
stats.req_todo += s.req_todo;
|
||||||
stats.req_started += s.req_started;
|
stats.req_started += s.req_started;
|
||||||
stats.req_done += s.req_done;
|
stats.req_done += s.req_done;
|
||||||
|
stats.req_timedout += s.req_timedout;
|
||||||
stats.req_success += s.req_success;
|
stats.req_success += s.req_success;
|
||||||
stats.req_status_success += s.req_status_success;
|
stats.req_status_success += s.req_status_success;
|
||||||
stats.req_failed += s.req_failed;
|
stats.req_failed += s.req_failed;
|
||||||
|
@ -1697,7 +1799,8 @@ finished in )" << util::format_duration(duration) << ", " << rps << " req/s, "
|
||||||
requests: )" << stats.req_todo << " total, " << stats.req_started
|
requests: )" << stats.req_todo << " total, " << stats.req_started
|
||||||
<< " started, " << stats.req_done << " done, "
|
<< " started, " << stats.req_done << " done, "
|
||||||
<< stats.req_status_success << " succeeded, " << stats.req_failed
|
<< stats.req_status_success << " succeeded, " << stats.req_failed
|
||||||
<< " failed, " << stats.req_error << R"( errored
|
<< " failed, " << stats.req_error << " errored, "
|
||||||
|
<< stats.req_timedout << R"( timeout
|
||||||
status codes: )" << stats.status[2] << " 2xx, " << stats.status[3] << " 3xx, "
|
status codes: )" << stats.status[2] << " 2xx, " << stats.status[3] << " 3xx, "
|
||||||
<< stats.status[4] << " 4xx, " << stats.status[5] << R"( 5xx
|
<< stats.status[4] << " 4xx, " << stats.status[5] << R"( 5xx
|
||||||
traffic: )" << stats.bytes_total << " bytes total, " << stats.bytes_head
|
traffic: )" << stats.bytes_total << " bytes total, " << stats.bytes_head
|
||||||
|
|
11
src/h2load.h
11
src/h2load.h
|
@ -81,6 +81,10 @@ struct Config {
|
||||||
size_t rate;
|
size_t rate;
|
||||||
// number of connections made
|
// number of connections made
|
||||||
size_t nconns;
|
size_t nconns;
|
||||||
|
// amount of time to wait for activity on a given connection
|
||||||
|
ssize_t conn_active_timeout;
|
||||||
|
// amount of time to wait after the last request is made on a connection
|
||||||
|
ssize_t conn_inactivity_timeout;
|
||||||
enum { PROTO_HTTP2, PROTO_SPDY2, PROTO_SPDY3, PROTO_SPDY3_1 } no_tls_proto;
|
enum { PROTO_HTTP2, PROTO_SPDY2, PROTO_SPDY3, PROTO_SPDY3_1 } no_tls_proto;
|
||||||
// file descriptor for upload data
|
// file descriptor for upload data
|
||||||
int data_fd;
|
int data_fd;
|
||||||
|
@ -144,6 +148,8 @@ struct Stats {
|
||||||
// The number of requests failed due to network errors. This is
|
// The number of requests failed due to network errors. This is
|
||||||
// subset of req_failed.
|
// subset of req_failed.
|
||||||
size_t req_error;
|
size_t req_error;
|
||||||
|
// The number of requests that failed due to timeout.
|
||||||
|
size_t req_timedout;
|
||||||
// The number of bytes received on the "wire". If SSL/TLS is used,
|
// The number of bytes received on the "wire". If SSL/TLS is used,
|
||||||
// this is the number of decrypted bytes the application received.
|
// this is the number of decrypted bytes the application received.
|
||||||
int64_t bytes_total;
|
int64_t bytes_total;
|
||||||
|
@ -214,6 +220,8 @@ struct Client {
|
||||||
size_t req_done;
|
size_t req_done;
|
||||||
int fd;
|
int fd;
|
||||||
Buffer<64_k> wb;
|
Buffer<64_k> wb;
|
||||||
|
ev_timer conn_active_watcher;
|
||||||
|
ev_timer conn_inactivity_watcher;
|
||||||
|
|
||||||
enum { ERR_CONNECT_FAIL = -100 };
|
enum { ERR_CONNECT_FAIL = -100 };
|
||||||
|
|
||||||
|
@ -222,7 +230,10 @@ struct Client {
|
||||||
int connect();
|
int connect();
|
||||||
void disconnect();
|
void disconnect();
|
||||||
void fail();
|
void fail();
|
||||||
|
void timeout();
|
||||||
|
void restart_timeout();
|
||||||
void submit_request();
|
void submit_request();
|
||||||
|
void process_timedout_streams();
|
||||||
void process_abandoned_streams();
|
void process_abandoned_streams();
|
||||||
void report_progress();
|
void report_progress();
|
||||||
void report_tls_info();
|
void report_tls_info();
|
||||||
|
|
Loading…
Reference in New Issue