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
|
||||
likely the network level failures or stream was reset by
|
||||
RST_STREAM.
|
||||
timeout
|
||||
The number of requests whose connection timed out before they
|
||||
were completed.
|
||||
|
||||
status codes
|
||||
The number of status code h2load received.
|
||||
|
|
121
src/h2load.cc
121
src/h2load.cc
|
@ -74,8 +74,9 @@ namespace h2load {
|
|||
Config::Config()
|
||||
: data_length(-1), addrs(nullptr), nreqs(1), nclients(1), nthreads(1),
|
||||
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),
|
||||
default_port(0), verbose(false) {}
|
||||
rate(0), nconns(0), conn_active_timeout(0), conn_inactivity_timeout(0),
|
||||
no_tls_proto(PROTO_HTTP2), data_fd(-1), port(0), default_port(0),
|
||||
verbose(false) {}
|
||||
|
||||
Config::~Config() {
|
||||
freeaddrinfo(addrs);
|
||||
|
@ -118,14 +119,16 @@ RequestStat::RequestStat() : data_offset(0), completed(false) {}
|
|||
|
||||
Stats::Stats(size_t req_todo)
|
||||
: 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),
|
||||
bytes_head(0), bytes_body(0), status(), req_stats(req_todo) {}
|
||||
req_status_success(0), req_failed(0), req_error(0), req_timedout(0),
|
||||
bytes_total(0), bytes_head(0), bytes_body(0), status(),
|
||||
req_stats(req_todo) {}
|
||||
|
||||
Stream::Stream() : status_success(-1) {}
|
||||
|
||||
namespace {
|
||||
void writecb(struct ev_loop *loop, ev_io *w, int revents) {
|
||||
auto client = static_cast<Client *>(w->data);
|
||||
client->restart_timeout();
|
||||
auto rv = client->do_write();
|
||||
if (rv == Client::ERR_CONNECT_FAIL) {
|
||||
client->disconnect();
|
||||
|
@ -145,6 +148,7 @@ void writecb(struct ev_loop *loop, ev_io *w, int revents) {
|
|||
namespace {
|
||||
void readcb(struct ev_loop *loop, ev_io *w, int revents) {
|
||||
auto client = static_cast<Client *>(w->data);
|
||||
client->restart_timeout();
|
||||
if (client->do_read() != 0) {
|
||||
client->fail();
|
||||
return;
|
||||
|
@ -178,6 +182,22 @@ void second_timeout_w_cb(struct ev_loop *loop, ev_timer *w, int revents) {
|
|||
}
|
||||
} // 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)
|
||||
: worker(worker), ssl(nullptr), next_addr(config.addrs), reqidx(0),
|
||||
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;
|
||||
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(); }
|
||||
|
@ -197,6 +225,10 @@ int Client::do_write() { return writefn(*this); }
|
|||
int Client::connect() {
|
||||
record_start_time(&worker->stats);
|
||||
|
||||
if (worker->config->conn_inactivity_timeout > 0) {
|
||||
ev_timer_again(worker->loop, &conn_inactivity_watcher);
|
||||
}
|
||||
|
||||
while (next_addr) {
|
||||
auto addr = next_addr;
|
||||
next_addr = next_addr->ai_next;
|
||||
|
@ -244,6 +276,18 @@ int Client::connect() {
|
|||
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() {
|
||||
process_abandoned_streams();
|
||||
|
||||
|
@ -251,6 +295,9 @@ void Client::fail() {
|
|||
}
|
||||
|
||||
void Client::disconnect() {
|
||||
ev_timer_stop(worker->loop, &conn_inactivity_watcher);
|
||||
ev_timer_stop(worker->loop, &conn_active_watcher);
|
||||
|
||||
streams.clear();
|
||||
session.reset();
|
||||
state = CLIENT_IDLE;
|
||||
|
@ -274,6 +321,25 @@ void Client::submit_request() {
|
|||
auto req_stat = &worker->stats.req_stats[worker->stats.req_started++];
|
||||
session->submit_request(req_stat);
|
||||
++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() {
|
||||
|
@ -1078,7 +1144,8 @@ Options:
|
|||
Available protocol: )";
|
||||
#endif // !HAVE_SPDYLAY
|
||||
out << NGHTTP2_CLEARTEXT_PROTO_VERSION_ID << R"(
|
||||
Default: )" << NGHTTP2_CLEARTEXT_PROTO_VERSION_ID << R"(
|
||||
Default: )"
|
||||
<< NGHTTP2_CLEARTEXT_PROTO_VERSION_ID << R"(
|
||||
-d, --data=<PATH>
|
||||
Post FILE to server. The request method is changed to
|
||||
POST.
|
||||
|
@ -1100,10 +1167,26 @@ Options:
|
|||
to make the -n requests specified. The default value
|
||||
for this option is 0. The -n option is not required if
|
||||
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
|
||||
Output debug information.
|
||||
--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
|
||||
|
||||
|
@ -1137,10 +1220,12 @@ int main(int argc, char **argv) {
|
|||
{"ciphers", required_argument, &flag, 2},
|
||||
{"rate", required_argument, nullptr, 'r'},
|
||||
{"num-conns", required_argument, nullptr, 'C'},
|
||||
{"connection-active-timeout", required_argument, nullptr, 'T'},
|
||||
{"connection-inactivity-timeout", required_argument, nullptr, 'N'},
|
||||
{nullptr, 0, nullptr, 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,
|
||||
&option_index);
|
||||
auto c = getopt_long(argc, argv, "hvW:c:d:m:n:p:t:w:H:i:r:C:T:N:",
|
||||
long_options, &option_index);
|
||||
if (c == -1) {
|
||||
break;
|
||||
}
|
||||
|
@ -1251,6 +1336,22 @@ int main(int argc, char **argv) {
|
|||
exit(EXIT_FAILURE);
|
||||
}
|
||||
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':
|
||||
config.verbose = true;
|
||||
break;
|
||||
|
@ -1656,6 +1757,7 @@ int main(int argc, char **argv) {
|
|||
stats.req_todo += s.req_todo;
|
||||
stats.req_started += s.req_started;
|
||||
stats.req_done += s.req_done;
|
||||
stats.req_timedout += s.req_timedout;
|
||||
stats.req_success += s.req_success;
|
||||
stats.req_status_success += s.req_status_success;
|
||||
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
|
||||
<< " started, " << stats.req_done << " done, "
|
||||
<< 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, "
|
||||
<< stats.status[4] << " 4xx, " << stats.status[5] << R"( 5xx
|
||||
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;
|
||||
// number of connections made
|
||||
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;
|
||||
// file descriptor for upload data
|
||||
int data_fd;
|
||||
|
@ -144,6 +148,8 @@ struct Stats {
|
|||
// The number of requests failed due to network errors. This is
|
||||
// subset of req_failed.
|
||||
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,
|
||||
// this is the number of decrypted bytes the application received.
|
||||
int64_t bytes_total;
|
||||
|
@ -214,6 +220,8 @@ struct Client {
|
|||
size_t req_done;
|
||||
int fd;
|
||||
Buffer<64_k> wb;
|
||||
ev_timer conn_active_watcher;
|
||||
ev_timer conn_inactivity_watcher;
|
||||
|
||||
enum { ERR_CONNECT_FAIL = -100 };
|
||||
|
||||
|
@ -222,7 +230,10 @@ struct Client {
|
|||
int connect();
|
||||
void disconnect();
|
||||
void fail();
|
||||
void timeout();
|
||||
void restart_timeout();
|
||||
void submit_request();
|
||||
void process_timedout_streams();
|
||||
void process_abandoned_streams();
|
||||
void report_progress();
|
||||
void report_tls_info();
|
||||
|
|
Loading…
Reference in New Issue