public Requester(CommandLineOptions options) { var requestHandler = new WebRequestHandler() { UseCookies = false, UseDefaultCredentials = true }; if (options.UseProxy) { requestHandler.UseProxy = true; requestHandler.Proxy = WebRequest.GetSystemWebProxy(); } _client = new HttpClient(requestHandler); _options = options; _url = new TokenisedString(options.Url); if (!string.IsNullOrEmpty(options.Template)) { _templateParser = new TemplateParser(File.ReadAllText(options.Template)); _randomValueProvider = new RandomValueProvider( new[] { _url }.Concat(_templateParser.Headers.Select(x => x.Value)).ToArray()); } else { _randomValueProvider = new RandomValueProvider(_url); } _valueProvider = new NoValueProvider(); if (!string.IsNullOrEmpty(_options.Plugin)) // plugin { var assembly = Assembly.LoadFile(_options.Plugin); var valueProviderType = assembly.GetExportedTypes().Where(t => typeof (IValueProvider) .IsAssignableFrom(t)).FirstOrDefault(); if(valueProviderType==null) throw new ArgumentException("No public type in plugin implements IValueProvider."); _valueProvider = (IValueProvider)Activator.CreateInstance(valueProviderType); } else if (!string.IsNullOrEmpty(options.ValuesFile)) // csv values file { _valueProvider = new SeparatedFileValueProvider(options.ValuesFile, options.IsTsv ? '\t' : ','); } }
static void Main(string[] args) { // to cover our back for all those fire and forgets TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException; Console.ForegroundColor = ConsoleColor.Gray; ThreadPool.SetMinThreads(200, 100); ThreadPool.SetMaxThreads(1000, 200); var statusCodes = new ConcurrentBag<HttpStatusCode>(); var commandLineOptions = new CommandLineOptions(); bool isHelp = args.Any(x => x == "-?"); var success = Parser.Default.ParseArguments(args, commandLineOptions); var then = DateTime.Now; ConsoleWriteLine(ConsoleColor.DarkCyan, "Starting at {0}", then) ; if (!success || isHelp) { if (!isHelp && args.Length > 0) ConsoleWriteLine(ConsoleColor.Red, "error parsing command line"); return; } try { var requester = new Requester(commandLineOptions); var writer = new StreamWriter(commandLineOptions.LogFile) { AutoFlush = true }; var stopwatch = Stopwatch.StartNew(); var timeTakens = new ConcurrentBag<double>(); if (commandLineOptions.SaveResponses) { if (string.IsNullOrEmpty(commandLineOptions.ResponseFolder)) { commandLineOptions.ResponseFolder = Path.Combine(Environment.CurrentDirectory, "Responses"); } if (!Directory.Exists(commandLineOptions.ResponseFolder)) Directory.CreateDirectory(commandLineOptions.ResponseFolder); } ConsoleWriteLine(ConsoleColor.Yellow, "[Press C to stop the test]"); int total = 0; bool disrupted = false; var stop = new ConsoleKeyInfo(); Console.ForegroundColor = ConsoleColor.Cyan; var source = new CancellationTokenSource(TimeSpan.FromDays(7)); Task.Run(() => { stop = Console.ReadKey(true); disrupted = true; }, source.Token); var result = Parallel.For(0, commandLineOptions.IsDryRun ? 1 : commandLineOptions.NumberOfRequests, new ParallelOptions() { MaxDegreeOfParallelism = commandLineOptions.Concurrency }, (i, loopstate) => { if (disrupted) { ConsoleWriteLine(ConsoleColor.Red, "..."); ConsoleWriteLine(ConsoleColor.Green, "Exiting.... please wait! (it might throw a few more requests)"); ConsoleWriteLine(ConsoleColor.Red, ""); loopstate.Stop(); source.Cancel(); } var sw = Stopwatch.StartNew(); IDictionary<string, object> parameters; var statusCode = requester.Next(i, out parameters); sw.Stop(); if (commandLineOptions.DelayInMillisecond > 0) { Thread.Sleep(commandLineOptions.DelayInMillisecond); } statusCodes.Add(statusCode); timeTakens.Add(sw.ElapsedTicks); var n = Interlocked.Increment(ref total); // fire and forget not to affect time taken or TPS Task.Run(() => WriteLine(writer, n, (int)statusCode, sw.ElapsedMilliseconds, parameters)); if (!commandLineOptions.Verbose) Console.Write("\r" + total); } ); stopwatch.Stop(); double[] orderedList = (from x in timeTakens orderby x select x).ToArray<double>(); Console.WriteLine(); ConsoleWriteLine(ConsoleColor.Magenta, "---------------Finished!----------------"); var now = DateTime.Now; ConsoleWriteLine(ConsoleColor.DarkCyan, "Finished at {0} (took {1})", now, now - then); // ----- adding stats of statuses returned var stats = statusCodes.GroupBy(x => x) .Select(y => new { Status = y.Key, Count = y.Count() }).OrderByDescending(z => z.Count); foreach (var stat in stats) { int statusCode = (int)stat.Status; if (statusCode >= 400 && statusCode < 600) { ConsoleWriteLine(ConsoleColor.Red, string.Format("Status {0}: {1}", statusCode, stat.Count)); } else { ConsoleWriteLine(ConsoleColor.Green, string.Format("Status {0}: {1}", statusCode, stat.Count)); } } Console.WriteLine(); Console.ForegroundColor = ConsoleColor.Yellow; Console.Write("TPS: " + Math.Round(total * 1000f / stopwatch.ElapsedMilliseconds, 1)); Console.WriteLine(" (requests/second)"); Console.WriteLine("Max: " + (timeTakens.Max() * 1000 / Stopwatch.Frequency) + "ms"); Console.WriteLine("Min: " + (timeTakens.Min() * 1000 / Stopwatch.Frequency) + "ms"); Console.WriteLine("Avg: " + (timeTakens.Average() * 1000 / Stopwatch.Frequency) + "ms"); Console.ForegroundColor = ConsoleColor.DarkGreen; Console.WriteLine(); Console.WriteLine(" 50%\tbelow " + Math.Round((double)((orderedList.Percentile<double>(50M) * 1000.0) / ((double)Stopwatch.Frequency))) + "ms"); Console.WriteLine(" 60%\tbelow " + Math.Round((double)((orderedList.Percentile<double>(60M) * 1000.0) / ((double)Stopwatch.Frequency))) + "ms"); Console.WriteLine(" 70%\tbelow " + Math.Round((double)((orderedList.Percentile<double>(70M) * 1000.0) / ((double)Stopwatch.Frequency))) + "ms"); Console.WriteLine(" 80%\tbelow " + Math.Round((double)((orderedList.Percentile<double>(80M) * 1000.0) / ((double)Stopwatch.Frequency))) + "ms"); Console.WriteLine(" 90%\tbelow " + Math.Round((double)((orderedList.Percentile<double>(90M) * 1000.0) / ((double)Stopwatch.Frequency))) + "ms"); Console.WriteLine(" 95%\tbelow " + Math.Round((double)((orderedList.Percentile<double>(95M) * 1000.0) / ((double)Stopwatch.Frequency))) + "ms"); Console.WriteLine(" 98%\tbelow " + Math.Round((double)((orderedList.Percentile<double>(98M) * 1000.0) / ((double)Stopwatch.Frequency))) + "ms"); Console.WriteLine(" 99%\tbelow " + Math.Round((double)((orderedList.Percentile<double>(99M) * 1000.0) / ((double)Stopwatch.Frequency))) + "ms"); Console.WriteLine("99.9%\tbelow " + Math.Round((double)((orderedList.Percentile<double>(99.9M) * 1000.0) / ((double)Stopwatch.Frequency))) + "ms"); } catch (Exception exception) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine(exception); } Console.ResetColor(); }