diff --git a/src/Microsoft.AspNet.SignalR.DCrank.Crank/Agent.cs b/src/Microsoft.AspNet.SignalR.DCrank.Crank/Agent.cs index 1cd07b2..9cbaa2f 100755 --- a/src/Microsoft.AspNet.SignalR.DCrank.Crank/Agent.cs +++ b/src/Microsoft.AspNet.SignalR.DCrank.Crank/Agent.cs @@ -173,7 +173,7 @@ public void StopWorker(int workerId) } } - public void StopWorkers() + public async Task StopWorkers() { var keys = _workers.Keys.ToList(); @@ -182,12 +182,18 @@ public void StopWorkers() AgentWorker worker; if (_workers.TryGetValue(key, out worker)) { - worker.Worker.Stop(); - Runner.LogAgent("Agent stopped Worker {0}.", key); + await worker.Worker.Stop(); + await Runner.LogAgent("Agent stopped Worker {0}.", key); } } TotalConnectionsRequested = 0; ApplyingLoad = false; + + // Wait for workers to terminate + while (_workers.Count > 0) + { + await Task.Delay(1000); + } } public async Task Pong(int id, int value) diff --git a/src/Microsoft.AspNet.SignalR.DCrank.Crank/DCrankArguments.cs b/src/Microsoft.AspNet.SignalR.DCrank.Crank/DCrankArguments.cs index ec6c640..20eab22 100644 --- a/src/Microsoft.AspNet.SignalR.DCrank.Crank/DCrankArguments.cs +++ b/src/Microsoft.AspNet.SignalR.DCrank.Crank/DCrankArguments.cs @@ -8,7 +8,7 @@ public class DCrankArguments [CommandLineParameter(Command = "?", Name = "Help", Default = false, Description = "Show Help", IsHelp = true)] public bool Help { get; set; } - [CommandLineParameter(Command = "Mode", Required = true, Description = "DCrank operating mode (Agent or Worker).")] + [CommandLineParameter(Command = "Mode", Required = false, Default = "commandline",Description = "DCrank operating mode (CommandLine, Agent, or Worker).")] public string Mode { get; set; } [CommandLineParameter(Command = "ControllerUrl", Required = false, Default = "http://localhost:17063", Description = "URL for Test Controller (Agent mode only).")] @@ -16,5 +16,17 @@ public class DCrankArguments [CommandLineParameter(Command = "ParentPid", Required = false, Description = "Process ID of calling agent (Worker mode only).")] public int ParentPid { get; set; } + + [CommandLineParameter(Command = "TargetUrl", Required = false, Default = "http://localhost:24037/", Description = "The URL for the test target (CommandLine mode only).")] + public string TargetUrl { get; set; } + + [CommandLineParameter(Command = "Workers", Required = false, Default = 1, Description = "Number of worker processes to create (CommandLine mode only).")] + public int Workers { get; set; } + + [CommandLineParameter(Command="Connections", Required = false, Default = 100000, Description = "The number of connections to establish with the test target (CommandLine mode only).")] + public int Connections { get; set; } + + [CommandLineParameter(Command = "SendDuration", Required = false, Default = 300, Description = "(Send phase) Duration in seconds. Default: 300 seconds")] + public int SendDuration { get; set; } } } diff --git a/src/Microsoft.AspNet.SignalR.DCrank.Crank/Microsoft.AspNet.SignalR.DCrank.Crank.csproj b/src/Microsoft.AspNet.SignalR.DCrank.Crank/Microsoft.AspNet.SignalR.DCrank.Crank.csproj index 169c44f..e20daa2 100644 --- a/src/Microsoft.AspNet.SignalR.DCrank.Crank/Microsoft.AspNet.SignalR.DCrank.Crank.csproj +++ b/src/Microsoft.AspNet.SignalR.DCrank.Crank/Microsoft.AspNet.SignalR.DCrank.Crank.csproj @@ -61,6 +61,7 @@ + diff --git a/src/Microsoft.AspNet.SignalR.DCrank.Crank/Program.cs b/src/Microsoft.AspNet.SignalR.DCrank.Crank/Program.cs index 3a5119d..4a89a0a 100644 --- a/src/Microsoft.AspNet.SignalR.DCrank.Crank/Program.cs +++ b/src/Microsoft.AspNet.SignalR.DCrank.Crank/Program.cs @@ -12,14 +12,14 @@ static void Main(string[] args) var arguments = CommandLine.Parse(); switch (arguments.Mode.ToLowerInvariant()) { + case "commandline": + StartCommandLine(arguments); + break; case "agent": - var agent = new Agent(); - var runner = new HubRunner(agent, arguments.ControllerUrl); - runner.Run().Wait(); + StartAgent(arguments); break; case "worker": - var worker = new Worker(arguments.ParentPid); - worker.Run().Wait(); + StartWorker(arguments); break; default: throw new ArgumentException(string.Format("Invalid value for Mode \"{0}\"", arguments.Mode)); @@ -35,5 +35,25 @@ static void Main(string[] args) Console.WriteLine(ex.Message); } } + + private static void StartCommandLine(DCrankArguments arguments) + { + var agent = new Agent(); + var runner = new Runner(agent, arguments); + runner.Run().Wait(); + } + + private static void StartAgent(DCrankArguments arguments) + { + var agent = new Agent(); + var runner = new HubRunner(agent, arguments.ControllerUrl); + runner.Run().Wait(); + } + + private static void StartWorker(DCrankArguments arguments) + { + var worker = new Worker(arguments.ParentPid); + worker.Run().Wait(); + } } } diff --git a/src/Microsoft.AspNet.SignalR.DCrank.Crank/Runner.cs b/src/Microsoft.AspNet.SignalR.DCrank.Crank/Runner.cs new file mode 100644 index 0000000..36be2f3 --- /dev/null +++ b/src/Microsoft.AspNet.SignalR.DCrank.Crank/Runner.cs @@ -0,0 +1,87 @@ +using System; +using System.Diagnostics; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace Microsoft.AspNet.SignalR.DCrank.Crank +{ + public class Runner : IRunner + { + private readonly Agent _agent; + private readonly string _targetUrl; + private readonly int _numberOfWorkers; + private readonly int _numberOfConnections; + private readonly int _sendDurationSeconds; + + public Runner(Agent agent, DCrankArguments arguments) + { + _agent = agent; + _targetUrl = arguments.TargetUrl; + _numberOfWorkers = arguments.Workers; + _numberOfConnections = arguments.Connections; + _sendDurationSeconds = arguments.SendDuration; + } + + public async Task Run() + { + _agent.Runner = this; + + var connectionsPerWorker = _numberOfConnections / _numberOfWorkers; + _agent.StartWorkers(_targetUrl, _numberOfWorkers, connectionsPerWorker); + + // Begin writing worker status information + var writeStatusCts = new CancellationTokenSource(); + var writeStatusTask = WriteConnectionStatus(writeStatusCts.Token); + + // Wait until all connections are connected + while (_agent.GetWorkerStatus().Aggregate(0, (state, status) => state + status.Value.ConnectedCount) < + _agent.TotalConnectionsRequested) + { + await Task.Delay(1000); + } + + // Stay connected for the duration of the send phase + await Task.Delay(TimeSpan.FromSeconds(_sendDurationSeconds)); + + // Disconnect + await _agent.StopWorkers(); + + // Stop writing worker status information + writeStatusCts.Cancel(); + await writeStatusTask; + } + + private async Task WriteConnectionStatus(CancellationToken cancellationToken) + { + await Task.Run(async () => + { + while (!cancellationToken.IsCancellationRequested) + { + var statusDictionary = _agent.GetWorkerStatus(); + foreach (var key in statusDictionary.Keys) + { + Trace.WriteLine(string.Format("({0}) {1}", key, JsonConvert.SerializeObject(statusDictionary[key]))); + } + await Task.Delay(1000); + } + }); + } + + public Task PongWorker(int workerId, int value) + { + throw new NotImplementedException(); + } + + public async Task LogAgent(string format, params object[] arguments) + { + Trace.WriteLine(string.Format(format, arguments)); + } + + public async Task LogWorker(int workerId, string format, params object[] arguments) + { + Trace.WriteLine(string.Format("({0}) {1}", workerId, string.Format(format, arguments))); + } + } +} diff --git a/src/Microsoft.AspNet.SignalR.DCrank.Crank/Worker.cs b/src/Microsoft.AspNet.SignalR.DCrank.Crank/Worker.cs index cd7b898..aeb1b1b 100755 --- a/src/Microsoft.AspNet.SignalR.DCrank.Crank/Worker.cs +++ b/src/Microsoft.AspNet.SignalR.DCrank.Crank/Worker.cs @@ -160,7 +160,13 @@ await _agent.Status( ); // Sending once per 5 seconds to avoid overloading the Test Controller - await Task.Delay(5000, cancellationToken); + try + { + await Task.Delay(5000, cancellationToken); + } + catch (TaskCanceledException) + { + } } } }