예제 #1
0
        /// <summary>
        /// Runs the specified uris concurrently and reports on the results for them.
        /// </summary>
        /// <param name="client">The HTTP client.</param>
        /// <param name="uris">The uris to run.</param>
        /// <param name="uriRunnerOptions">The options for the runner.</param>
        /// <returns>The results for the uri.</returns>
        public static Task<UriRunnerResult[]> RunUrisConcurrentlyAsync(HttpClient client, IReadOnlyList<string> uris, UriRunnerOptions uriRunnerOptions)
        {
            var tasks = new Task<UriRunnerResult>[uris.Count];

            Parallel.For(
                fromInclusive: 0, 
                toExclusive: uris.Count, 
                body: (i, state) =>
                {
                    var uri = uris[i];
                    tasks[i] = RunUriAsync(
                        client: client, 
                        uri: uri,
                        uriRunnerOptions: uriRunnerOptions);
                });

            return Task.WhenAll(tasks);
        }
예제 #2
0
        /// <summary>
        /// Runs the specified URI asynchronously and reports timing results.
        /// </summary>
        /// <param name="client">The HTTP client.</param>
        /// <param name="uri">The URI to run.</param>
        /// <param name="uriRunnerOptions">The options.</param>
        /// <returns>The timing result.</returns>
        private static async Task<UriRunnerResult> RunUriAsync(HttpClient client, string uri, UriRunnerOptions uriRunnerOptions)
        {
            var sw = new Stopwatch();
            sw.Start();

            using (var response = await client.GetAsync(uri, completionOption: HttpCompletionOption.ResponseHeadersRead))
            {
                var responseTime = sw.Elapsed;
                var statusCode = response.StatusCode;

                // Do not need to get the body if we don't need to calculate body size or checksum.
                if (!uriRunnerOptions.CalculateBodyChecksum && !uriRunnerOptions.CalculateBodySize)
                {
                    // Optimistically get the body size from the headers if it's there.
                    var contentLength = response.Content.Headers.ContentLength;

                    return new UriRunnerResult(
                        uri: uri,
                        responseTime: responseTime,
                        statusCode: statusCode,
                        bodySize: contentLength.GetValueOrDefault(),
                        bodyChecksum: "0");
                }

                using (var httpContent = response.Content)
                {
                    var body = await httpContent.ReadAsByteArrayAsync();
                    var bodySize = body.Length;
                    var md5 = MD5.Create();
                    var hash = md5.ComputeHash(body);
                    var bodyChecksum = ByteArrayToHexString(hash);

                    return new UriRunnerResult(
                        uri: uri,
                        responseTime: responseTime,
                        statusCode: statusCode,
                        bodySize: bodySize,
                        bodyChecksum: bodyChecksum);
                }
            }
        }
예제 #3
0
        /// <summary>
        /// Mains entry-point.
        /// </summary>
        /// <param name="args">The command-line arguments. We expect none at the moment.</param>
        public static void Main(string[] args)
        {
            // Just to make sure there isn't anything fuffing around with the number of concurrent requests.
            System.Net.ServicePointManager.DefaultConnectionLimit = int.MaxValue;

            var consoleWriteLine = StringWriterFuns.ConsoleWriter();

            var options = new CommandLineOptions();

            if (!CommandLine.Parser.Default.ParseArgumentsStrict(args, options))
            {
                consoleWriteLine("Something is wrong with command-line arguments.");
                Environment.Exit(-1);
            }

            var replayFileNames  = options.ExpandedReplayFiles();
            var replayFiles      = new ReplayFile[replayFileNames.Length];
            var replayFileErrors = 0;

            for (var i = 0; i < replayFileNames.Length; i++)
            {
                var replayFileName = replayFileNames[i];

                try
                {
                    var content = File.ReadAllText(replayFileName);
                    content = Environment.ExpandEnvironmentVariables(content);
                    var replay = Newtonsoft.Json.JsonConvert.DeserializeObject <ReplayFile>(content);
                    replayFiles[i] = replay;

                    consoleWriteLine("Yay!: {0}", replayFileName);
                    consoleWriteLine("    - {0}", replay.Name);
                    consoleWriteLine("    - {0}", replay.Description);
                    consoleWriteLine("    - {0}", replay.BaseUri);
                    consoleWriteLine("    - {0}", replay.Headers);
                    consoleWriteLine("    - {0} uris", replay.Uris.Length.ToString("G"));
                }
                catch (Exception e)
                {
                    consoleWriteLine("**** ERROR reading replay file: {0}", replayFileName);
                    consoleWriteLine(e.ToString());
                    replayFileErrors++;
                }
            }

            if (replayFileErrors > 0)
            {
                consoleWriteLine("+++++ Could not read all replay files. Exiting.");
                Environment.Exit(-1);
            }

            if (replayFiles.Length == 0)
            {
                consoleWriteLine("+++++ No suitable replay files.");
                Environment.Exit(-1);
            }

            if (options.OutFile != null && File.Exists(options.OutFile))
            {
                File.Delete(options.OutFile);
            }

            StringWriterFuns.WriteLineFun writeReportLine;
            if (options.OutFile != null)
            {
                writeReportLine = StringWriterFuns.AggregateWriter(
                    new[]
                {
                    StringWriterFuns.ConsoleWriter(),
                    StringWriterFuns.FileWriter(options.OutFile)
                });
            }
            else
            {
                writeReportLine = StringWriterFuns.ConsoleWriter();
            }

            // The CSV header with field names.
            const string Header = "Sequence, Iterations, Concurrent Requests, TestStartTimeUtc, TestEndTimeUtc, TestDurationMs, Status Code, Response Size, Response Checksum, Req/Sec, min, perc50, perc75, perc90, perc95, max, description, baseUri, uri";

            writeReportLine(Header);

            // The callback for completed HTTP request. We use it here to
            // write the CSV result into the file. Keep the sequence number
            // so that we can print that out too.
            var sequenceNumber = 0;
            Action <ReplayFilerRunnerResult> callback = (r) =>
            {
                sequenceNumber++;

                var line = string.Format(
                    "{0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, {9}, {10}, {11}, {12}, {13}, {14}, {15}, {16}, {17}, {18}",
                    sequenceNumber.ToString("G"),
                    r.Iterations.ToString("G"),
                    r.ConcurrentRequests.ToString("G"),
                    r.TestStartTime.ToUniversalTime().ToString("yyy-mm-dd HH:MM:ss.fff"),
                    r.TestEndTime.ToUniversalTime().ToString("yyy-mm-dd HH:MM:ss.fff"),
                    r.TestDuration.TotalMilliseconds.ToString("F"),
                    r.LastStatusCode.ToString("G"),
                    r.LastResponseBodySize.ToString("G"),
                    r.LastResponseBodyChecksum,
                    r.RequestsPerSec.ToString("F"),
                    r.ResponseTimeMin.TotalMilliseconds.ToString("G"),
                    r.ResponseTimePerc50.TotalMilliseconds.ToString("G"),
                    r.ResponseTimePerc75.TotalMilliseconds.ToString("G"),
                    r.ResponseTimePerc90.TotalMilliseconds.ToString("G"),
                    r.ResponseTimePerc95.TotalMilliseconds.ToString("G"),
                    r.ResponseTimeMax.TotalMilliseconds.ToString("G"),
                    r.Description,
                    r.BaseUri,
                    r.Uri);

                writeReportLine(line);
            };

            foreach (var replayFile in replayFiles)
            {
                var uriRunnerOptions = new UriRunnerOptions(
                    calculateBodySize: options.CalculateBodySize,
                    calculateBodyChecksum: options.CalculateBodyChecksum);

                ReplayFileRunner.RunReplayFileAsync(
                    replayFile: replayFile,
                    callback: callback,
                    uriRunnerOptions: uriRunnerOptions,
                    iterations: options.Iterations,
                    concurrentRequests: options.Concurrency).Wait();
            }
        }
예제 #4
0
        /// <summary>
        /// Asynchronously runs the specified replay file.
        /// </summary>
        /// <param name="replayFile">The replay file.</param>
        /// <param name="callback">The callback to call at the completion of each of the request in the replay file..</param>
        /// <param name="uriRunnerOptions">The options for the uri runner.</param>
        /// <param name="iterations">The iterations.</param>
        /// <param name="concurrentRequests">The number of concurrent requests to execute.</param>
        /// <returns>The list of the results.</returns>
        public static async Task RunReplayFileAsync(
            ReplayFile replayFile, 
            Action<ReplayFilerRunnerResult> callback, 
            UriRunnerOptions uriRunnerOptions, 
            int iterations = 1, 
            int concurrentRequests = 1)
        {
            var consoleWriteLine = StringWriterFuns.ConsoleWriter();

            using (var handler = new HttpClientHandler())
            using (var client = new HttpClient(handler))
            {
                handler.AllowAutoRedirect = true;
                handler.AutomaticDecompression = DecompressionMethods.None;
                handler.UseProxy = true;

                // Need to set this to false to make sure the http client does not
                // mess around with our own cookies.
                handler.UseCookies = false;

                client.BaseAddress = new Uri(replayFile.BaseUri);
                foreach (var header in replayFile.Headers)
                {
                    if (!client.DefaultRequestHeaders.TryAddWithoutValidation(header.Key, header.Value))
                    {
                        consoleWriteLine("Failed to add header: {0} = {1}", header.Key, header.Value);
                    }
                }

                foreach (var uri in replayFile.Uris)
                {
                    var sw = new Stopwatch();

                    var numberOfRequests = iterations * concurrentRequests;
                    var allResults = new List<UriRunnerResult>(capacity: numberOfRequests);
                    var startTimeUtc = DateTimeOffset.UtcNow;

                    // Exec same URI with the specified concurrency level
                    var uris = Enumerable.Repeat(uri, concurrentRequests).ToArray();
                    for (var i = 0; i < iterations; i++)
                    {
                        sw.Start();
                        var runUrisResults = await UriRunner.RunUrisConcurrentlyAsync(
                            client: client, 
                            uris: uris,
                            uriRunnerOptions: uriRunnerOptions);
                        sw.Stop();
                        allResults.AddRange(runUrisResults);
                    }

                    var endTimeUtc = DateTimeOffset.UtcNow;
                    var requestsPerSec = (double)numberOfRequests / sw.Elapsed.TotalSeconds;
                    var responseTimes = allResults.Select(r => r.ResponseTime);
                    var percentiles = new PercentileStats<TimeSpan>(responseTimes);

                    var lastResult = allResults.Last();

                    var reportLine = new ReplayFilerRunnerResult(
                        iterations: iterations, 
                        concurrentRequests: concurrentRequests, 
                        lastStatusCode: (int)lastResult.StatusCode, 
                        lastResponseBodySize: lastResult.BodySize, 
                        lastResponseBodyChecksum: lastResult.BodyChecksum, 
                        responseTimeStats: percentiles, 
                        description: replayFile.Description, 
                        baseUri: replayFile.BaseUri, 
                        uri: uri,
                        requestsPerSec: requestsPerSec,
                        testStartTime: startTimeUtc,
                        testEndTime: endTimeUtc);

                    if (callback != null)
                    {
                        callback(reportLine);
                    }
                }
            }
        }
예제 #5
0
        /// <summary>
        /// Runs the specified URI asynchronously and reports timing results.
        /// </summary>
        /// <param name="client">The HTTP client.</param>
        /// <param name="uri">The URI to run.</param>
        /// <param name="uriRunnerOptions">The options.</param>
        /// <returns>The timing result.</returns>
        private static async Task <UriRunnerResult> RunUriAsync(HttpClient client, string uri, UriRunnerOptions uriRunnerOptions)
        {
            var sw = new Stopwatch();

            sw.Start();

            using (var response = await client.GetAsync(uri, completionOption: HttpCompletionOption.ResponseHeadersRead))
            {
                var responseTime = sw.Elapsed;
                var statusCode   = response.StatusCode;

                // Do not need to get the body if we don't need to calculate body size or checksum.
                if (!uriRunnerOptions.CalculateBodyChecksum && !uriRunnerOptions.CalculateBodySize)
                {
                    // Optimistically get the body size from the headers if it's there.
                    var contentLength = response.Content.Headers.ContentLength;

                    return(new UriRunnerResult(
                               uri: uri,
                               responseTime: responseTime,
                               statusCode: statusCode,
                               bodySize: contentLength.GetValueOrDefault(),
                               bodyChecksum: "0"));
                }

                using (var httpContent = response.Content)
                {
                    var body = await httpContent.ReadAsByteArrayAsync();

                    var bodySize     = body.Length;
                    var md5          = MD5.Create();
                    var hash         = md5.ComputeHash(body);
                    var bodyChecksum = ByteArrayToHexString(hash);

                    return(new UriRunnerResult(
                               uri: uri,
                               responseTime: responseTime,
                               statusCode: statusCode,
                               bodySize: bodySize,
                               bodyChecksum: bodyChecksum));
                }
            }
        }
예제 #6
0
        /// <summary>
        /// Runs the specified uris concurrently and reports on the results for them.
        /// </summary>
        /// <param name="client">The HTTP client.</param>
        /// <param name="uris">The uris to run.</param>
        /// <param name="uriRunnerOptions">The options for the runner.</param>
        /// <returns>The results for the uri.</returns>
        public static Task <UriRunnerResult[]> RunUrisConcurrentlyAsync(HttpClient client, IReadOnlyList <string> uris, UriRunnerOptions uriRunnerOptions)
        {
            var tasks = new Task <UriRunnerResult> [uris.Count];

            Parallel.For(
                fromInclusive: 0,
                toExclusive: uris.Count,
                body: (i, state) =>
            {
                var uri  = uris[i];
                tasks[i] = RunUriAsync(
                    client: client,
                    uri: uri,
                    uriRunnerOptions: uriRunnerOptions);
            });

            return(Task.WhenAll(tasks));
        }
예제 #7
0
        /// <summary>
        /// Asynchronously runs the specified replay file.
        /// </summary>
        /// <param name="replayFile">The replay file.</param>
        /// <param name="callback">The callback to call at the completion of each of the request in the replay file..</param>
        /// <param name="uriRunnerOptions">The options for the uri runner.</param>
        /// <param name="iterations">The iterations.</param>
        /// <param name="concurrentRequests">The number of concurrent requests to execute.</param>
        /// <returns>The list of the results.</returns>
        public static async Task RunReplayFileAsync(
            ReplayFile replayFile,
            Action <ReplayFilerRunnerResult> callback,
            UriRunnerOptions uriRunnerOptions,
            int iterations         = 1,
            int concurrentRequests = 1)
        {
            var consoleWriteLine = StringWriterFuns.ConsoleWriter();

            using (var handler = new HttpClientHandler())
                using (var client = new HttpClient(handler))
                {
                    handler.AllowAutoRedirect      = true;
                    handler.AutomaticDecompression = DecompressionMethods.None;
                    handler.UseProxy = true;

                    // Need to set this to false to make sure the http client does not
                    // mess around with our own cookies.
                    handler.UseCookies = false;

                    client.BaseAddress = new Uri(replayFile.BaseUri);
                    foreach (var header in replayFile.Headers)
                    {
                        if (!client.DefaultRequestHeaders.TryAddWithoutValidation(header.Key, header.Value))
                        {
                            consoleWriteLine("Failed to add header: {0} = {1}", header.Key, header.Value);
                        }
                    }

                    foreach (var uri in replayFile.Uris)
                    {
                        var sw = new Stopwatch();

                        var numberOfRequests = iterations * concurrentRequests;
                        var allResults       = new List <UriRunnerResult>(capacity: numberOfRequests);
                        var startTimeUtc     = DateTimeOffset.UtcNow;

                        // Exec same URI with the specified concurrency level
                        var uris = Enumerable.Repeat(uri, concurrentRequests).ToArray();
                        for (var i = 0; i < iterations; i++)
                        {
                            sw.Start();
                            var runUrisResults = await UriRunner.RunUrisConcurrentlyAsync(
                                client : client,
                                uris : uris,
                                uriRunnerOptions : uriRunnerOptions);

                            sw.Stop();
                            allResults.AddRange(runUrisResults);
                        }

                        var endTimeUtc     = DateTimeOffset.UtcNow;
                        var requestsPerSec = (double)numberOfRequests / sw.Elapsed.TotalSeconds;
                        var responseTimes  = allResults.Select(r => r.ResponseTime);
                        var percentiles    = new PercentileStats <TimeSpan>(responseTimes);

                        var lastResult = allResults.Last();

                        var reportLine = new ReplayFilerRunnerResult(
                            iterations: iterations,
                            concurrentRequests: concurrentRequests,
                            lastStatusCode: (int)lastResult.StatusCode,
                            lastResponseBodySize: lastResult.BodySize,
                            lastResponseBodyChecksum: lastResult.BodyChecksum,
                            responseTimeStats: percentiles,
                            description: replayFile.Description,
                            baseUri: replayFile.BaseUri,
                            uri: uri,
                            requestsPerSec: requestsPerSec,
                            testStartTime: startTimeUtc,
                            testEndTime: endTimeUtc);

                        if (callback != null)
                        {
                            callback(reportLine);
                        }
                    }
                }
        }
예제 #8
0
        /// <summary>
        /// Mains entry-point.
        /// </summary>
        /// <param name="args">The command-line arguments. We expect none at the moment.</param>
        public static void Main(string[] args)
        {
            // Just to make sure there isn't anything fuffing around with the number of concurrent requests.
            System.Net.ServicePointManager.DefaultConnectionLimit = int.MaxValue;

            var consoleWriteLine = StringWriterFuns.ConsoleWriter();

            var options = new CommandLineOptions();
            if (!CommandLine.Parser.Default.ParseArgumentsStrict(args, options))
            {
                consoleWriteLine("Something is wrong with command-line arguments.");
                Environment.Exit(-1);
            }

            var replayFileNames = options.ExpandedReplayFiles();
            var replayFiles = new ReplayFile[replayFileNames.Length];
            var replayFileErrors = 0;
            for (var i = 0; i < replayFileNames.Length; i++)
            {
                var replayFileName = replayFileNames[i];

                try
                {
                    var content = File.ReadAllText(replayFileName);
                    content = Environment.ExpandEnvironmentVariables(content);
                    var replay = Newtonsoft.Json.JsonConvert.DeserializeObject<ReplayFile>(content);
                    replayFiles[i] = replay;

                    consoleWriteLine("Yay!: {0}", replayFileName);
                    consoleWriteLine("    - {0}", replay.Name);
                    consoleWriteLine("    - {0}", replay.Description);
                    consoleWriteLine("    - {0}", replay.BaseUri);
                    consoleWriteLine("    - {0}", replay.Headers);
                    consoleWriteLine("    - {0} uris", replay.Uris.Length.ToString("G"));
                }
                catch (Exception e)
                {
                    consoleWriteLine("**** ERROR reading replay file: {0}", replayFileName);
                    consoleWriteLine(e.ToString());
                    replayFileErrors++;
                }
            }

            if (replayFileErrors > 0)
            {
                consoleWriteLine("+++++ Could not read all replay files. Exiting.");
                Environment.Exit(-1);
            }

            if (replayFiles.Length == 0)
            {
                consoleWriteLine("+++++ No suitable replay files.");
                Environment.Exit(-1);
            }

            if (options.OutFile != null && File.Exists(options.OutFile))
            {
                File.Delete(options.OutFile);
            }

            StringWriterFuns.WriteLineFun writeReportLine;
            if (options.OutFile != null)
            {
                writeReportLine = StringWriterFuns.AggregateWriter(
                    new[]
                    {
                        StringWriterFuns.ConsoleWriter(),
                        StringWriterFuns.FileWriter(options.OutFile)
                    });
            }
            else
            {
                writeReportLine = StringWriterFuns.ConsoleWriter();
            }

            // The CSV header with field names.
            const string Header = "Sequence, Iterations, Concurrent Requests, TestStartTimeUtc, TestEndTimeUtc, TestDurationMs, Status Code, Response Size, Response Checksum, Req/Sec, min, perc50, perc75, perc90, perc95, max, description, baseUri, uri";
            writeReportLine(Header);

            // The callback for completed HTTP request. We use it here to
            // write the CSV result into the file. Keep the sequence number
            // so that we can print that out too.
            var sequenceNumber = 0;
            Action<ReplayFilerRunnerResult> callback = (r) =>
            {
                sequenceNumber++;

                var line = string.Format(
                    "{0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, {9}, {10}, {11}, {12}, {13}, {14}, {15}, {16}, {17}, {18}",
                    sequenceNumber.ToString("G"),
                    r.Iterations.ToString("G"),
                    r.ConcurrentRequests.ToString("G"),
                    r.TestStartTime.ToUniversalTime().ToString("yyy-mm-dd HH:MM:ss.fff"),
                    r.TestEndTime.ToUniversalTime().ToString("yyy-mm-dd HH:MM:ss.fff"),
                    r.TestDuration.TotalMilliseconds.ToString("F"),
                    r.LastStatusCode.ToString("G"),
                    r.LastResponseBodySize.ToString("G"),
                    r.LastResponseBodyChecksum,
                    r.RequestsPerSec.ToString("F"),
                    r.ResponseTimeMin.TotalMilliseconds.ToString("G"),
                    r.ResponseTimePerc50.TotalMilliseconds.ToString("G"),
                    r.ResponseTimePerc75.TotalMilliseconds.ToString("G"),
                    r.ResponseTimePerc90.TotalMilliseconds.ToString("G"),
                    r.ResponseTimePerc95.TotalMilliseconds.ToString("G"),
                    r.ResponseTimeMax.TotalMilliseconds.ToString("G"),
                    r.Description,
                    r.BaseUri,
                    r.Uri);

                writeReportLine(line);
            };

            foreach (var replayFile in replayFiles)
            {
                var uriRunnerOptions = new UriRunnerOptions(
                    calculateBodySize: options.CalculateBodySize,
                    calculateBodyChecksum: options.CalculateBodyChecksum);

                ReplayFileRunner.RunReplayFileAsync(
                    replayFile: replayFile,
                    callback: callback,
                    uriRunnerOptions: uriRunnerOptions,
                    iterations: options.Iterations,
                    concurrentRequests: options.Concurrency).Wait();
            }
        }