h2load adding timeout options

This commit is contained in:
Nora 2015-08-18 01:52:22 -04:00
parent 71623b674e
commit 727ba4906c
3 changed files with 126 additions and 9 deletions

View File

@ -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.

View File

@ -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

View File

@ -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();