HTTP Benchmarking Tool Written In Node.JS
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

218 lines
4.8 KiB

var gauss = require('gauss');
var url_parse = require('url').parse;
var iniparser = require('iniparser');
var extend = require('xtend');
var http = require('http');
var fs = require('fs');
var async = require('async');
var argv = require('optimist')
.usage('Usage: $0 [config] [config]...')
.options('c', {
alias: 'concurrency',
default: 1,
describe: 'Number of Concurrent Clients to Use'
})
.options('r', {
alias: 'requests',
describe: 'Total Number Of Requests To Make'
})
.options('k', {
alias: 'keepalive',
describe: 'Whether Or Not To Enable Keep Alive Support',
boolean: true,
default: false,
})
.options('l', {
alias: 'log',
describe: 'Log File To Write All Request Data To',
string: true,
})
.options('s', {
alias: 'stats',
describe: 'Whether or Not Print Result Stats',
boolean: true,
default: true
})
.options('q', {
alias: 'quiet',
default: false,
describe: 'Whether Or Not To Silence stdout',
boolean: true,
})
.options('h', {
alias: 'help',
describe: 'Show This Help Text',
boolean: true
})
.demand(1)
.argv;
if( argv.help ){
require('optimist').showHelp();
process.exit();
}
if( argv.quiet ){
console.log = function(msg){};
}
if( argv.log ){
argv.log = fs.openSync(argv.log, 'w');
}
var config = {
random: false,
urls: {},
};
var defaults = {
num: 1,
port: 80,
};
argv._.forEach(function(file){
try{
ini = iniparser.parseSync(file);
for( var key in ini ){
if( typeof(ini[key]) === 'object'){
var url = key;
if(url.substring(0, 4) !== 'http'){
url = 'http://' + url;
}
parts = url_parse(url, true, true);
ini[key] = extend({}, defaults, ini[key], parts);
} else{
config[key] = ini[key];
delete ini[key];
}
}
config.urls = extend(config.urls, ini);
}catch(e){
console.error('Could Not Process ' + file + ' ini File');
console.error(e.stack);
process.exit(1);
}
});
var urls = [];
for( var key in config.urls ){
var url = config.urls[key];
for( var i = 0; i < url.num; ++i ){
urls.push(url);
}
}
if( typeof(config.random) !== 'boolean' ){
config.random = config.random.toLowerCase() === 'true';
}
if( config.random ){
console.log('Randomizing Url List');
var i = urls.length;
if ( i == 0 ) return false;
while ( --i ) {
var j = Math.floor( Math.random() * ( i + 1 ) );
var tempi = urls[i];
var tempj = urls[j];
urls[i] = tempj;
urls[j] = tempi;
}
}
if( !argv.requests ){
argv.requests = urls.length;
}
var results = [];
var errors = [];
var save_result = function(result){
results.push(result);
if( argv.log ){
var tmp = [];
for( var i in result ){
var val = parseInt(result[i]);
if( isNaN(val) ){
tmp.push('"' + result[i] + '"');
} else{
tmp.push(val);
}
}
fs.writeSync(argv.log, tmp.join() + '\r\n');
}
}
var run = function(options, done){
var start = new Date().getTime();
req = http.request(options, function(res){
var stop = new Date().getTime();
save_result({
ms: (stop - start),
status: res.statusCode,
url: options.href
});
done();
});
req.on('error', function(err){
save_error({
error: err,
url: options.href
});
done();
});
if( options.data ){
req.write(options.data);
}
req.setSocketKeepAlive(argv.keepalive);
req.end();
};
console.log('Starting Up ' + argv.concurrency + ' Clients');
var queue = async.queue(run, argv.concurrency);
var percentiles = [.7, .8, .85, .9, .94, .96, .99];
queue.drain = function(){
console.log('Finished');
if( argv.stats ){
console.log('Processing Results');
var tmp = results;
results = {};
tmp.forEach( function(result){
var key = result.url;
delete result.url;
if( results[key] == undefined ){
results[key] = []
}
results[key].push(result);
});
for( var url in results ){
console.log('Results For: ' + url);
console.log('\tTotal Requests: ' + results[url].length);
var ms_data = results[url].map( function(a){ return a.ms; } );
ms_data = ms_data.sort();
var ms_set = new gauss.Vector(ms_data);
console.log('\tMean Time (ms): ' + ms_set.mean());
console.log('\tMin Time (ms): ' + ms_set.min());
console.log('\tMax Time (ms): ' + ms_set.max());
console.log('\tTotal Time (ms): ' + ms_set.sum());
percentiles.forEach( function(percent){
console.log('\t' + (percent*100) + 'th Percentile: ' + ms_set.percentile(percent));
});
}
}
if( argv.log ){
fs.closeSync(argv.log);
}
process.exit(0);
};
console.log('Queuing Up ' + argv.requests + ' Requests');
var offset = 0;
for( var i = 0; i < argv.requests; ++i ){
if( offset >= urls.length ){
offset = 0;
}
queue.push(urls[offset]);
offset += 1;
}