Crankier server (#12406)
* move server into Crankier * added ConnectionCounter * allow logging
This commit is contained in:
parent
6699353dc6
commit
6b4a101ca7
|
|
@ -2,6 +2,7 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Http.Connections;
|
using Microsoft.AspNetCore.Http.Connections;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.SignalR.Crankier.Commands
|
namespace Microsoft.AspNetCore.SignalR.Crankier.Commands
|
||||||
{
|
{
|
||||||
|
|
@ -11,5 +12,6 @@ namespace Microsoft.AspNetCore.SignalR.Crankier.Commands
|
||||||
public static readonly int NumberOfConnections = 10_000;
|
public static readonly int NumberOfConnections = 10_000;
|
||||||
public static readonly int SendDurationInSeconds = 300;
|
public static readonly int SendDurationInSeconds = 300;
|
||||||
public static readonly HttpTransportType TransportType = HttpTransportType.WebSockets;
|
public static readonly HttpTransportType TransportType = HttpTransportType.WebSockets;
|
||||||
|
public static readonly LogLevel LogLevel = LogLevel.None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Http.Connections;
|
||||||
|
using Microsoft.Extensions.CommandLineUtils;
|
||||||
|
using static Microsoft.AspNetCore.SignalR.Crankier.Commands.CommandLineUtilities;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.AspNetCore.SignalR.Crankier.Server;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.SignalR.Crankier.Commands
|
||||||
|
{
|
||||||
|
internal class ServerCommand
|
||||||
|
{
|
||||||
|
public static void Register(CommandLineApplication app)
|
||||||
|
{
|
||||||
|
app.Command("server", cmd =>
|
||||||
|
{
|
||||||
|
var logLevelOption = cmd.Option("--log <LOG_LEVEL>", "The LogLevel to use.", CommandOptionType.SingleValue);
|
||||||
|
|
||||||
|
cmd.OnExecute(() =>
|
||||||
|
{
|
||||||
|
LogLevel logLevel = Defaults.LogLevel;
|
||||||
|
|
||||||
|
if (logLevelOption.HasValue() && !Enum.TryParse(logLevelOption.Value(), out logLevel))
|
||||||
|
{
|
||||||
|
return InvalidArg(logLevelOption);
|
||||||
|
}
|
||||||
|
return Execute(logLevel);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int Execute(LogLevel logLevel)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Process ID: {Process.GetCurrentProcess().Id}");
|
||||||
|
|
||||||
|
var config = new ConfigurationBuilder()
|
||||||
|
.AddEnvironmentVariables(prefix: "ASPNETCORE_")
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
var host = new WebHostBuilder()
|
||||||
|
.UseConfiguration(config)
|
||||||
|
.ConfigureLogging(loggerFactory =>
|
||||||
|
{
|
||||||
|
loggerFactory.AddConsole().SetMinimumLevel(logLevel);
|
||||||
|
})
|
||||||
|
.UseKestrel()
|
||||||
|
.UseStartup<Startup>();
|
||||||
|
|
||||||
|
host.Build().Run();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -9,6 +9,11 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="Microsoft.AspNetCore.SignalR.Client" />
|
<Reference Include="Microsoft.AspNetCore.SignalR.Client" />
|
||||||
<Reference Include="Microsoft.Extensions.CommandLineUtils.Sources" />
|
<Reference Include="Microsoft.Extensions.CommandLineUtils.Sources" />
|
||||||
|
<Reference Include="Microsoft.AspNetCore.SignalR" />
|
||||||
|
<Reference Include="Microsoft.AspNetCore.Server.Kestrel" />
|
||||||
|
<Reference Include="Microsoft.Extensions.Configuration.CommandLine" />
|
||||||
|
<Reference Include="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" />
|
||||||
|
<Reference Include="Microsoft.Extensions.Logging.Console" />
|
||||||
<Reference Include="Newtonsoft.Json" />
|
<Reference Include="Newtonsoft.Json" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ namespace Microsoft.AspNetCore.SignalR.Crankier
|
||||||
LocalCommand.Register(app);
|
LocalCommand.Register(app);
|
||||||
AgentCommand.Register(app);
|
AgentCommand.Register(app);
|
||||||
WorkerCommand.Register(app);
|
WorkerCommand.Register(app);
|
||||||
|
ServerCommand.Register(app);
|
||||||
|
|
||||||
app.Command("help", cmd =>
|
app.Command("help", cmd =>
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.SignalR.Crankier.Server
|
||||||
|
{
|
||||||
|
public class ConnectionCounter
|
||||||
|
{
|
||||||
|
private int _totalConnectedCount;
|
||||||
|
private int _peakConnectedCount;
|
||||||
|
private int _totalDisconnectedCount;
|
||||||
|
private int _receivedCount;
|
||||||
|
|
||||||
|
private readonly object _lock = new object();
|
||||||
|
|
||||||
|
public ConnectionSummary Summary
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
return new ConnectionSummary
|
||||||
|
{
|
||||||
|
CurrentConnections = _totalConnectedCount - _totalDisconnectedCount,
|
||||||
|
PeakConnections = _peakConnectedCount,
|
||||||
|
TotalConnected = _totalConnectedCount,
|
||||||
|
TotalDisconnected = _totalDisconnectedCount,
|
||||||
|
ReceivedCount = _receivedCount
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Receive(string payload)
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
_receivedCount += payload.Length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Connected()
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
_totalConnectedCount++;
|
||||||
|
_peakConnectedCount = Math.Max(_totalConnectedCount - _totalDisconnectedCount, _peakConnectedCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Disconnected()
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
_totalDisconnectedCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,79 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.SignalR.Crankier.Server
|
||||||
|
{
|
||||||
|
public class ConnectionCounterHostedService : IHostedService, IDisposable
|
||||||
|
{
|
||||||
|
private Stopwatch _timeSinceFirstConnection;
|
||||||
|
private readonly ConnectionCounter _counter;
|
||||||
|
private ConnectionSummary _lastSummary;
|
||||||
|
private Timer _timer;
|
||||||
|
private int _executingDoWork;
|
||||||
|
|
||||||
|
public ConnectionCounterHostedService(ConnectionCounter counter)
|
||||||
|
{
|
||||||
|
_counter = counter;
|
||||||
|
_timeSinceFirstConnection = new Stopwatch();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task StartAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
_timer = new Timer(DoWork, null, TimeSpan.Zero, TimeSpan.FromSeconds(1));
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DoWork(object state)
|
||||||
|
{
|
||||||
|
if (Interlocked.Exchange(ref _executingDoWork, 1) == 0)
|
||||||
|
{
|
||||||
|
var summary = _counter.Summary;
|
||||||
|
|
||||||
|
if (summary.PeakConnections > 0)
|
||||||
|
{
|
||||||
|
if (_timeSinceFirstConnection.ElapsedTicks == 0)
|
||||||
|
{
|
||||||
|
_timeSinceFirstConnection.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
var elapsed = _timeSinceFirstConnection.Elapsed;
|
||||||
|
|
||||||
|
if (_lastSummary != null)
|
||||||
|
{
|
||||||
|
Console.WriteLine(@"[{0:hh\:mm\:ss}] Current: {1}, peak: {2}, connected: {3}, disconnected: {4}, rate: {5}/s",
|
||||||
|
elapsed,
|
||||||
|
summary.CurrentConnections,
|
||||||
|
summary.PeakConnections,
|
||||||
|
summary.TotalConnected - _lastSummary.TotalConnected,
|
||||||
|
summary.TotalDisconnected - _lastSummary.TotalDisconnected,
|
||||||
|
summary.CurrentConnections - _lastSummary.CurrentConnections
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
_lastSummary = summary;
|
||||||
|
}
|
||||||
|
|
||||||
|
Interlocked.Exchange(ref _executingDoWork, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task StopAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
_timer?.Change(Timeout.Infinite, 0);
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_timer?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.SignalR.Crankier.Server
|
||||||
|
{
|
||||||
|
public class ConnectionSummary
|
||||||
|
{
|
||||||
|
public int TotalConnected { get; set; }
|
||||||
|
|
||||||
|
public int TotalDisconnected { get; set; }
|
||||||
|
|
||||||
|
public int PeakConnections { get; set; }
|
||||||
|
|
||||||
|
public int CurrentConnections { get; set; }
|
||||||
|
|
||||||
|
public int ReceivedCount { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.SignalR;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.SignalR.Crankier.Server
|
||||||
|
{
|
||||||
|
public class EchoHub : Hub
|
||||||
|
{
|
||||||
|
private ConnectionCounter _counter;
|
||||||
|
|
||||||
|
public EchoHub(ConnectionCounter counter)
|
||||||
|
{
|
||||||
|
_counter = counter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Broadcast(int duration)
|
||||||
|
{
|
||||||
|
var sent = 0;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var t = new CancellationTokenSource();
|
||||||
|
t.CancelAfter(TimeSpan.FromSeconds(duration));
|
||||||
|
while (!t.IsCancellationRequested && !Context.ConnectionAborted.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
await Clients.All.SendAsync("send", DateTime.UtcNow);
|
||||||
|
sent++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Console.WriteLine(e);
|
||||||
|
}
|
||||||
|
Console.WriteLine("Broadcast exited: Sent {0} messages", sent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Task OnConnectedAsync()
|
||||||
|
{
|
||||||
|
_counter?.Connected();
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Task OnDisconnectedAsync(Exception exception)
|
||||||
|
{
|
||||||
|
_counter?.Disconnected();
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DateTime Echo(DateTime time)
|
||||||
|
{
|
||||||
|
return time;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task EchoAll(DateTime time)
|
||||||
|
{
|
||||||
|
return Clients.All.SendAsync("send", time);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SendPayload(string payload)
|
||||||
|
{
|
||||||
|
_counter?.Receive(payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DateTime GetCurrentTime()
|
||||||
|
{
|
||||||
|
return DateTime.UtcNow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.SignalR.Crankier.Server
|
||||||
|
{
|
||||||
|
public class Startup
|
||||||
|
{
|
||||||
|
private readonly IConfiguration _config;
|
||||||
|
public Startup(IConfiguration configuration)
|
||||||
|
{
|
||||||
|
_config = configuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ConfigureServices(IServiceCollection services)
|
||||||
|
{
|
||||||
|
var signalrBuilder = services.AddSignalR()
|
||||||
|
.AddMessagePackProtocol();
|
||||||
|
|
||||||
|
services.AddSingleton<ConnectionCounter>();
|
||||||
|
|
||||||
|
services.AddHostedService<ConnectionCounterHostedService>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
||||||
|
{
|
||||||
|
app.UseRouting();
|
||||||
|
|
||||||
|
app.UseEndpoints(endpoints =>
|
||||||
|
{
|
||||||
|
endpoints.MapHub<EchoHub>("/echo");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue