ApacheBench
view release on metacpan or search on metacpan
lib/HTTPD/Bench/ApacheBench.pm view on Meta::CPAN
sub run {
my ($self, $run_no, $run) = @_;
return undef if ! (ref $self->{runs} eq 'ARRAY' && blessed $self->{runs}->[$run_no]
&& $self->{runs}->[$run_no]->isa('HTTPD::Bench::ApacheBench::Run'));
if (blessed $run && $run->isa('HTTPD::Bench::ApacheBench::Run')) {
my $replaced_run = $self->{runs}->[$run_no];
$self->{runs}->[$run_no] = $run;
return $replaced_run;
}
return $self->{runs}->[$run_no];
}
sub add_run {
my ($self, $newrun) = @_;
return undef if ! (ref $self->{runs} eq 'ARRAY' && blessed $newrun
&& $newrun->isa('HTTPD::Bench::ApacheBench::Run'));
push(@{$self->{runs}}, $newrun);
return $#{$self->{runs}};
}
sub delete_run {
my ($self, $run_no) = @_;
return undef if ref $self->{runs} ne 'ARRAY';
my $deleted_run = $self->{runs}->[$run_no];
$self->{runs} = [ @{$self->{runs}}[0..$run_no-1],
@{$self->{runs}}[$run_no+1..$#{$self->{runs}}] ];
return $deleted_run;
}
sub num_runs {
my ($self) = @_;
return scalar(@{$self->{runs} || []});
}
1;
__END__
=head1 NAME
HTTPD::Bench::ApacheBench - Perl API for Apache benchmarking and regression testing.
=head1 SYNOPSIS
use HTTPD::Bench::ApacheBench;
my $b = HTTPD::Bench::ApacheBench->new;
# global configuration
$b->concurrency(5);
$b->priority("run_priority");
# add HTTP request sequences (aka: runs)
my $run1 = HTTPD::Bench::ApacheBench::Run->new
({ urls => ["http://localhost/one", "http://localhost/two"] });
$b->add_run($run1);
my $run2 = HTTPD::Bench::ApacheBench::Run->new
({ urls => ["http://localhost/three", "http://localhost/four"],
cookies => ["Login_Cookie=b3dcc9bac34b7e60;"],
# note: manual cookies no longer necessary due to use_auto_cookies (enabled by default)
order => "depth_first",
repeat => 10,
memory => 2 });
$b->add_run($run2);
my $run3 = HTTPD::Bench::ApacheBench::Run->new
({ urls => ["http://localhost/five", "http://localhost/six"],
memory => 3,
postdata => [
sub($) { sleep(int(rand(5)) + 1); return undef; },
sub($) {
my $prev_response = shift;
sleep(int(rand(5)) + 1);
my ($username) = ( $prev_response =~ m|<div id="userName">([^<>]+)</div>|i );
return $username ? 'cgi-username='.$username : undef;
},
] });
$b->add_run($run3);
# send HTTP request sequences to server and time responses
my $ro = $b->execute;
# calculate hits/sec
print ((1000*$b->total_requests/$b->total_time)." req/sec\n");
# show request times (in ms) for $run1, 1st repetition
print join(', ', @{$run1->request_times}) . "\n";
# show response times (in ms) for $run2, 7th repetition
print join(', ', @{$run2->iteration(6)->response_times}) . "\n";
# dump the entire regression object (WARNING, this could be a LOT OF DATA)
use Data::Dumper;
my $d = Data::Dumper->new([$ro]);
print $d->Dumpxs;
=head1 GOALS
This project is meant to be the foundation of a complete benchmarking
and regression testing suite for an advanced, transaction-based mod_perl
site. We need to be able to stress our server to its limit while also
having a way to verify the HTTP responses for correctness. Since our site
is transaction-based (as opposed to content-based), we needed to extend
the single-URL ab model to a multiple-URL sequence model.
ApacheBench was originally based on the Apache 1.3.12 ab code
(src/support/ab.c), but has since undergone major rewrites and now barely
resembles ab.c.
Note: although this tool was designed to be used on an Apache mod_perl
site, it is generally applicable to any HTTP-compliant server. Beware,
however, that it sends a high volume of HTTP requests in a very short period
of time, which may overwhelm some weaker HTTP server implementations
like NT/IIS.
=head1 DESCRIPTION
ApacheBench sends sequences of HTTP requests to an HTTP server and keeps
lib/HTTPD/Bench/ApacheBench.pm view on Meta::CPAN
summary information about size of requests and
responses.
2 => Remember connect/response times and all
information about size of requests and responses.
Also keeps an array of all HTTP response
headers returned by the server for each request.
3 => Remember connect/response times, all information
about request/response sizes, HTTP response
headers, and also all content of every HTTP
response returned by the server. B<Warning>:
this can quickly eat up all available main
memory if used with large runs.
=item $b->add_run( $run_object )
Schedule a run for this ApacheBench object. Returns the run number where
this object was inserted, or undef on failure. See below for details on
$run_object.
=item $run = $b->run( $run_no, [$run_object] )
Returns the run object stored in location $run_no. If a run object is passed
as the optional second argument, it is stored in location $run_no, displacing
whatever was there. The displaced run object is then returned.
=item $b->delete_run( $run_no )
Delete the run object stored in location $run_no. The deleted run object
is returned to the caller for safety sake.
=item $b->num_runs()
Returns the number of runs currently configured in $b.
=back
=head2 Run configuration methods
You need to configure one or more benchmark runs. A run is defined as an
ordered sequence of HTTP requests which can be repeated multiple times,
and scheduled to be sent in different ways.
=over 4
=item $run = HTTPD::Bench::ApacheBench::Run->new({ urls => [ @url_list ] })
Construct a run object with the ordered sequence of HTTP requests
in @url_list.
=item $run->repeat( $n )
Number of times to repeat this request sequence.
(default: B<1>, or whatever is specified in global configuration)
=item $run->use_auto_cookies( 0|1 )
Controls whether to enable dynamic setting of cookies based on previous
response headers in this run. If set, will parse the Set-Cookie: headers
in each response in the run, and set the corresponding Cookie: headers in
all subsequent requests in this run. (basically a crude, but fast emulation
of a browser's cookie handling mechanism) The cookies are cumulative for
each iteration of the run, so they will accumulate with each request/response
pair until the next iteration, when they get reset.
(default: B<1>)
=item $run->cookies( \@cookies )
Set any extra HTTP Cookie: headers for each B<repetition> of this run.
Length of @cookies should equal $n (whatever you set $run->repeat to).
If this option is omitted, only auto-set cookies will be sent in
requests for this run.
If you need to set different cookies within a single URL sequence, use
the request_headers() method.
Note: this is somewhat obsolete now that there is support for dynamic
cookies, but is kept for backwards compatibility and in case you want to
add your own "static" cookies.
Example usage: You could simulate $n users all doing the same transaction
simultaneously by giving $n different login cookies here. Say you have
login cookies in an array called @login of length $n. Set $run->repeat($n),
$run->order("breadth_first"), $run->cookies([map {$login[$_]} 0..$n-1]), and
ApacheBench will perform the transaction sequence set by $run->urls $n times,
each with a separate login cookie.
=item $run->urls( \@url_list )
Set the HTTP request URLs for this run. A @url_list B<must> be given for
each run, otherwise the run will not execute. Typically @url_list is set
using the constructor.
=item $run->postdata( \@postdata )
Set the HTTP POST request body contents. If an undef value is encountered
in @postdata, that request will be a GET request (or a HEAD request if you
used $run->head_requests() below). If this option is omitted, all requests
for this run will be GET (or HEAD) requests.
Length of @postdata should equal the length of @url_list.
The @postdata array should consist of either strings or code refs (or undef).
A string value will make this request always a POST. An undef makes this
request always a GET. If a code ref, it will depend on the return value of
the called code. (see below for details)
The strings should contain exactly what you want sent in the HTTP POST request
body. For example,
@postdata = (undef, undef, 'cgikey1=val1&cgikey2=val2');
will send two GET requests, then a POST request with the CGI parameter
'cgikey1' set to 'val1' and the CGI parameter 'cgikey2' set to 'val2'.
The code refs should be references to subroutines that take one string
argument, which will be the http response (both headers and body) returned
from the *previous* request in this run, and return CGI post data as above.
If a subroutine returns undef, then the request will be a GET rather than a
POST. If the return value is a string (even empty string), the request sent
will be a POST with the string as the request body.
For example,
@postdata = (undef, sub($) {
my $prev_response = shift;
my ($username) = ( $prev_response =~ m|<div id="userName">([^<>]+)</div>|i );
return $username ? 'cgi-username='.$username : undef;
});
( run in 2.115 seconds using v1.01-cache-2.11-cpan-13bb782fe5a )