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.
|
||||
|
||||
using Microsoft.AspNetCore.Http.Connections;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
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 SendDurationInSeconds = 300;
|
||||
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>
|
||||
<Reference Include="Microsoft.AspNetCore.SignalR.Client" />
|
||||
<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" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ namespace Microsoft.AspNetCore.SignalR.Crankier
|
|||
LocalCommand.Register(app);
|
||||
AgentCommand.Register(app);
|
||||
WorkerCommand.Register(app);
|
||||
ServerCommand.Register(app);
|
||||
|
||||
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