diff --git a/orleans/GPSTracker/GPSTracker.AppHost/GPSTracker.AppHost.csproj b/orleans/GPSTracker/GPSTracker.AppHost/GPSTracker.AppHost.csproj new file mode 100644 index 00000000000..1bf637d69c9 --- /dev/null +++ b/orleans/GPSTracker/GPSTracker.AppHost/GPSTracker.AppHost.csproj @@ -0,0 +1,25 @@ + + + + + + Exe + net9.0 + enable + enable + true + afde51c8-abcf-4e8d-97a3-fd9301b96ffd + + + + + + + + + + + + + + diff --git a/orleans/GPSTracker/GPSTracker.AppHost/Program.cs b/orleans/GPSTracker/GPSTracker.AppHost/Program.cs new file mode 100644 index 00000000000..be426d35447 --- /dev/null +++ b/orleans/GPSTracker/GPSTracker.AppHost/Program.cs @@ -0,0 +1,19 @@ +var builder = DistributedApplication.CreateBuilder(args); + +// https://learn.microsoft.com/en-us/dotnet/aspire/frameworks/orleans?tabs=dotnet-cli +var storage = builder.AddAzureStorage("storage") + .RunAsEmulator(); +var clusteringTable = storage.AddTables("clustering"); +var orleans = builder.AddOrleans("default") + .WithClustering(clusteringTable); + +var service = builder.AddProject("gpstracker-service") + .WithReference(orleans) + .WithReplicas(3); + +var deviceGateway = builder.AddProject("device-gateway") + .WithReference(orleans.AsClient()) + .WithExternalHttpEndpoints() + .WaitFor(service); + +builder.Build().Run(); diff --git a/orleans/GPSTracker/GPSTracker.AppHost/Properties/launchSettings.json b/orleans/GPSTracker/GPSTracker.AppHost/Properties/launchSettings.json new file mode 100644 index 00000000000..a384ae369e6 --- /dev/null +++ b/orleans/GPSTracker/GPSTracker.AppHost/Properties/launchSettings.json @@ -0,0 +1,29 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:17244;http://localhost:15133", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:21285", + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:22279" + } + }, + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "http://localhost:15133", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:19294", + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:20112" + } + } + } +} diff --git a/orleans/GPSTracker/GPSTracker.AppHost/appsettings.Development.json b/orleans/GPSTracker/GPSTracker.AppHost/appsettings.Development.json new file mode 100644 index 00000000000..0c208ae9181 --- /dev/null +++ b/orleans/GPSTracker/GPSTracker.AppHost/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/orleans/GPSTracker/GPSTracker.AppHost/appsettings.json b/orleans/GPSTracker/GPSTracker.AppHost/appsettings.json new file mode 100644 index 00000000000..31c092aa450 --- /dev/null +++ b/orleans/GPSTracker/GPSTracker.AppHost/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning", + "Aspire.Hosting.Dcp": "Warning" + } + } +} diff --git a/orleans/GPSTracker/GPSTracker.Common/GPSTracker.Common.csproj b/orleans/GPSTracker/GPSTracker.Common/GPSTracker.Common.csproj index 5274db20142..bfce1d9e944 100644 --- a/orleans/GPSTracker/GPSTracker.Common/GPSTracker.Common.csproj +++ b/orleans/GPSTracker/GPSTracker.Common/GPSTracker.Common.csproj @@ -1,11 +1,11 @@  - net8.0 + net9.0 enable enable - + \ No newline at end of file diff --git a/orleans/GPSTracker/GPSTracker.Common/LoadDriver.cs b/orleans/GPSTracker/GPSTracker.Common/LoadDriver.cs index 4a8fea79004..3ca38970383 100644 --- a/orleans/GPSTracker/GPSTracker.Common/LoadDriver.cs +++ b/orleans/GPSTracker/GPSTracker.Common/LoadDriver.cs @@ -111,7 +111,7 @@ private static void UpdateDevicePosition(Model model, double delta) public static double NextDouble(double min, double max) => Random.NextDouble() * (max - min) + min; - private class Model + private sealed class Model { public Stopwatch TimeSinceLastUpdate { get; } = Stopwatch.StartNew(); public int DeviceId { get; set; } diff --git a/orleans/GPSTracker/GPSTracker.FakeDeviceGateway/GPSTracker.FakeDeviceGateway.csproj b/orleans/GPSTracker/GPSTracker.FakeDeviceGateway/GPSTracker.FakeDeviceGateway.csproj index f986c60fff6..b49cb511353 100644 --- a/orleans/GPSTracker/GPSTracker.FakeDeviceGateway/GPSTracker.FakeDeviceGateway.csproj +++ b/orleans/GPSTracker/GPSTracker.FakeDeviceGateway/GPSTracker.FakeDeviceGateway.csproj @@ -1,11 +1,16 @@  - net8.0 + net9.0 enable enable Exe + + + + + \ No newline at end of file diff --git a/orleans/GPSTracker/GPSTracker.FakeDeviceGateway/Program.cs b/orleans/GPSTracker/GPSTracker.FakeDeviceGateway/Program.cs index 29323f6ca91..f073dd6cf12 100644 --- a/orleans/GPSTracker/GPSTracker.FakeDeviceGateway/Program.cs +++ b/orleans/GPSTracker/GPSTracker.FakeDeviceGateway/Program.cs @@ -2,11 +2,12 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -using IHost host = Host.CreateDefaultBuilder(args) - .UseOrleansClient((ctx, clientBuilder) => clientBuilder.UseLocalhostClustering()) - .UseConsoleLifetime() - .Build(); +var builder = Host.CreateApplicationBuilder(args); +builder.AddServiceDefaults(); +builder.AddKeyedAzureTableClient("clustering"); +builder.UseOrleansClient(); +using var host = builder.Build(); await host.StartAsync(); IHostApplicationLifetime lifetime = host.Services.GetRequiredService(); diff --git a/orleans/GPSTracker/GPSTracker.Service/GPSTracker.Service.csproj b/orleans/GPSTracker/GPSTracker.Service/GPSTracker.Service.csproj index 619c89e22eb..7d29f0de57c 100644 --- a/orleans/GPSTracker/GPSTracker.Service/GPSTracker.Service.csproj +++ b/orleans/GPSTracker/GPSTracker.Service/GPSTracker.Service.csproj @@ -1,6 +1,6 @@ - + - net8.0 + net9.0 enable enable Exe @@ -8,13 +8,13 @@ + - - - - + + + \ No newline at end of file diff --git a/orleans/GPSTracker/GPSTracker.Service/Grains/DeviceGrain.cs b/orleans/GPSTracker/GPSTracker.Service/Grains/DeviceGrain.cs index f843c29e1df..79456d205cd 100644 --- a/orleans/GPSTracker/GPSTracker.Service/Grains/DeviceGrain.cs +++ b/orleans/GPSTracker/GPSTracker.Service/Grains/DeviceGrain.cs @@ -7,7 +7,7 @@ namespace GPSTracker.GrainImplementation; [Reentrant] public class DeviceGrain : Grain, IDeviceGrain { - private DeviceMessage _lastMessage = null!; + private DeviceMessage? _lastMessage; private readonly IPushNotifierGrain _pushNotifier; diff --git a/orleans/GPSTracker/GPSTracker.Service/Grains/HubListGrain.cs b/orleans/GPSTracker/GPSTracker.Service/Grains/HubListGrain.cs index 472c97f15c0..56da17566c7 100644 --- a/orleans/GPSTracker/GPSTracker.Service/Grains/HubListGrain.cs +++ b/orleans/GPSTracker/GPSTracker.Service/Grains/HubListGrain.cs @@ -2,18 +2,12 @@ namespace GPSTracker.GrainImplementation; -public class HubListGrain : Grain, IHubListGrain +public class HubListGrain(IClusterMembershipService clusterMembershipService) : Grain, IHubListGrain { - private readonly IClusterMembershipService _clusterMembership; - private readonly Dictionary _hubs = new(); + private readonly Dictionary _hubs = []; private MembershipVersion _cacheMembershipVersion; private List<(SiloAddress Host, IRemoteLocationHub Hub)>? _cache; - public HubListGrain(IClusterMembershipService clusterMembershipService) - { - _clusterMembership = clusterMembershipService; - } - public ValueTask AddHub(SiloAddress host, IRemoteLocationHub hubReference) { // Invalidate the cache. @@ -29,7 +23,7 @@ public ValueTask AddHub(SiloAddress host, IRemoteLocationHub hubReference) private List<(SiloAddress Host, IRemoteLocationHub Hub)> GetCachedHubs() { // Returns a cached list of hubs if the cache is valid, otherwise builds a list of hubs. - ClusterMembershipSnapshot clusterMembers = _clusterMembership.CurrentSnapshot; + ClusterMembershipSnapshot clusterMembers = clusterMembershipService.CurrentSnapshot; if (_cache is { } && clusterMembers.Version == _cacheMembershipVersion) { return _cache; diff --git a/orleans/GPSTracker/GPSTracker.Service/Grains/IHubListGrain.cs b/orleans/GPSTracker/GPSTracker.Service/Grains/IHubListGrain.cs index 066b43489e3..fc1c7c013fa 100644 --- a/orleans/GPSTracker/GPSTracker.Service/Grains/IHubListGrain.cs +++ b/orleans/GPSTracker/GPSTracker.Service/Grains/IHubListGrain.cs @@ -1,4 +1,4 @@ -using Orleans.Runtime; +using Orleans.Runtime; namespace GPSTracker.GrainImplementation; @@ -8,5 +8,6 @@ namespace GPSTracker.GrainImplementation; public interface IHubListGrain : IGrainWithGuidKey { ValueTask AddHub(SiloAddress host, IRemoteLocationHub hubReference); + ValueTask> GetHubs(); } diff --git a/orleans/GPSTracker/GPSTracker.Service/Grains/PushNotifierGrain.cs b/orleans/GPSTracker/GPSTracker.Service/Grains/PushNotifierGrain.cs index d179caebc8f..b2c3dc21b5e 100644 --- a/orleans/GPSTracker/GPSTracker.Service/Grains/PushNotifierGrain.cs +++ b/orleans/GPSTracker/GPSTracker.Service/Grains/PushNotifierGrain.cs @@ -7,44 +7,46 @@ namespace GPSTracker.GrainImplementation; [Reentrant] [StatelessWorker(maxLocalWorkers: 12)] -public class PushNotifierGrain : Grain, IPushNotifierGrain +public sealed class PushNotifierGrain(ILogger logger) : Grain, IPushNotifierGrain, IDisposable { private readonly Queue _messageQueue = new(); - private readonly ILogger _logger; private List<(SiloAddress Host, IRemoteLocationHub Hub)> _hubs = new(); - public PushNotifierGrain(ILogger logger) => _logger = logger; private Task _flushTask = Task.CompletedTask; + private IGrainTimer? _flushTimer; + private IGrainTimer? _refreshTimer; public override async Task OnActivateAsync(CancellationToken cancellationToken) { // Set up a timer to regularly flush the message queue - RegisterTimer( - _ => - { - Flush(); - return Task.CompletedTask; - }, - null, - TimeSpan.FromMilliseconds(15), - TimeSpan.FromMilliseconds(15)); + _flushTimer = this.RegisterGrainTimer( + ct => Flush(), + dueTime: TimeSpan.FromMilliseconds(15), + period: TimeSpan.FromMilliseconds(15)); // Set up a timer to regularly refresh the hubs, to respond to azure infrastructure changes await RefreshHubs(); - RegisterTimer( - asyncCallback: async _ => await RefreshHubs(), - state: null, + + _refreshTimer = this.RegisterGrainTimer( + async _ => await RefreshHubs(), dueTime: TimeSpan.FromSeconds(60), period: TimeSpan.FromSeconds(60)); await base.OnActivateAsync(cancellationToken); } + public override async Task OnDeactivateAsync(DeactivationReason deactivationReason, CancellationToken cancellationToken) { await Flush(); await base.OnDeactivateAsync(deactivationReason, cancellationToken); } + public void Dispose() + { + _flushTimer?.Dispose(); + _refreshTimer?.Dispose(); + } + private async ValueTask RefreshHubs() { // Discover the current infrastructure @@ -77,16 +79,14 @@ async Task FlushInternal() { // Send all messages to all SignalR hubs var messagesToSend = new List(Math.Min(_messageQueue.Count, MaxMessagesPerBatch)); - while (messagesToSend.Count < MaxMessagesPerBatch && _messageQueue.TryDequeue(out VelocityMessage? msg)) messagesToSend.Add(msg); - - var tasks = new List(_hubs.Count); - var batch = new VelocityBatch(messagesToSend); - - foreach ((SiloAddress Host, IRemoteLocationHub Hub) hub in _hubs) + while (messagesToSend.Count < MaxMessagesPerBatch && _messageQueue.TryDequeue(out VelocityMessage? msg)) { - tasks.Add(BroadcastUpdates(hub.Host, hub.Hub, batch, _logger)); + messagesToSend.Add(msg); } + var batch = new VelocityBatch(messagesToSend); + var tasks = _hubs.Select(hub => BroadcastUpdates(hub.Host, hub.Hub, batch, logger)); + await Task.WhenAll(tasks); } } diff --git a/orleans/GPSTracker/GPSTracker.Service/Grains/RemoteLocationHub.cs b/orleans/GPSTracker/GPSTracker.Service/Grains/RemoteLocationHub.cs index a81ec195263..8f6a994e4fc 100644 --- a/orleans/GPSTracker/GPSTracker.Service/Grains/RemoteLocationHub.cs +++ b/orleans/GPSTracker/GPSTracker.Service/Grains/RemoteLocationHub.cs @@ -6,14 +6,10 @@ namespace GPSTracker; /// /// Broadcasts location messages to clients which are connected to the local SignalR hub. /// -internal sealed class RemoteLocationHub : IRemoteLocationHub +internal sealed class RemoteLocationHub(IHubContext hub) : IRemoteLocationHub { - private readonly IHubContext _hub; - - public RemoteLocationHub(IHubContext hub) => _hub = hub; - // Send a message to every client which is connected to the hub public ValueTask BroadcastUpdates(VelocityBatch messages) => - new(_hub.Clients.All.SendAsync( + new(hub.Clients.All.SendAsync( "locationUpdates", messages, CancellationToken.None)); } diff --git a/orleans/GPSTracker/GPSTracker.Service/HubListUpdater.cs b/orleans/GPSTracker/GPSTracker.Service/HubListUpdater.cs index 6523bf84bb1..9efd2a9a18b 100644 --- a/orleans/GPSTracker/GPSTracker.Service/HubListUpdater.cs +++ b/orleans/GPSTracker/GPSTracker.Service/HubListUpdater.cs @@ -8,31 +8,19 @@ namespace GPSTracker; /// /// Periodically updates the implementation with a reference to the local . /// -[Reentrant] -internal sealed class HubListUpdater : BackgroundService +internal sealed class HubListUpdater( + IGrainFactory grainFactory, + ILogger logger, + ILocalSiloDetails localSiloDetails, + IHubContext hubContext) : BackgroundService { - private readonly IGrainFactory _grainFactory; - private readonly ILogger _logger; - private readonly ILocalSiloDetails _localSiloDetails; - private readonly RemoteLocationHub _locationBroadcaster; - - public HubListUpdater( - IGrainFactory grainFactory, - ILogger logger, - ILocalSiloDetails localSiloDetails, - IHubContext hubContext) - { - _grainFactory = grainFactory; - _logger = logger; - _localSiloDetails = localSiloDetails; - _locationBroadcaster = new RemoteLocationHub(hubContext); - } + private readonly RemoteLocationHub _locationBroadcaster = new RemoteLocationHub(hubContext); protected override async Task ExecuteAsync(CancellationToken stoppingToken) { - IHubListGrain hubListGrain = _grainFactory.GetGrain(Guid.Empty); - SiloAddress localSiloAddress = _localSiloDetails.SiloAddress; - IRemoteLocationHub selfReference = _grainFactory.CreateObjectReference(_locationBroadcaster); + IHubListGrain hubListGrain = grainFactory.GetGrain(Guid.Empty); + SiloAddress localSiloAddress = localSiloDetails.SiloAddress; + IRemoteLocationHub selfReference = grainFactory.CreateObjectReference(_locationBroadcaster); // This runs in a loop because the HubListGrain does not use any form of persistence, so if the // host which it is activated on stops, then it will lose any internal state. @@ -45,7 +33,7 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) } catch (Exception exception) when (!stoppingToken.IsCancellationRequested) { - _logger.LogError(exception, "Error polling location hub list"); + logger.LogError(exception, "Error polling location hub list"); } if (!stoppingToken.IsCancellationRequested) diff --git a/orleans/GPSTracker/GPSTracker.Service/Program.cs b/orleans/GPSTracker/GPSTracker.Service/Program.cs index d7ef77561bb..507940ee424 100644 --- a/orleans/GPSTracker/GPSTracker.Service/Program.cs +++ b/orleans/GPSTracker/GPSTracker.Service/Program.cs @@ -4,61 +4,25 @@ using OpenTelemetry.Trace; using System.Net; -WebApplicationBuilder builder = WebApplication.CreateBuilder(args); +var builder = WebApplication.CreateBuilder(args); -builder.Services.AddOpenTelemetry() - .WithMetrics(metrics => - { - metrics - .AddPrometheusExporter() - .AddMeter("Microsoft.Orleans"); - }) - .WithTracing(tracing => - { - // Set a service name - tracing.SetResourceBuilder( - ResourceBuilder.CreateDefault() - .AddService(serviceName: "GPSTracker", serviceVersion: "1.0")); - - tracing.AddSource("Microsoft.Orleans.Runtime"); - tracing.AddSource("Microsoft.Orleans.Application"); - - tracing.AddZipkinExporter(zipkin => - { - zipkin.Endpoint = new Uri("http://localhost:9411/api/v2/spans"); - }); - }); - -builder.Host.UseOrleans((ctx, siloBuilder) => { - - // In order to support multiple hosts forming a cluster, they must listen on different ports. - // Use the --InstanceId X option to launch subsequent hosts. - int instanceId = ctx.Configuration.GetValue("InstanceId"); - siloBuilder.UseLocalhostClustering( - siloPort: 11111 + instanceId, - gatewayPort: 30000 + instanceId, - primarySiloEndpoint: new IPEndPoint(IPAddress.Loopback, 11111)); - - siloBuilder.AddActivityPropagation(); -}); -builder.WebHost.UseKestrel((ctx, kestrelOptions) => -{ - // To avoid port conflicts, each Web server must listen on a different port. - int instanceId = ctx.Configuration.GetValue("InstanceId"); - kestrelOptions.ListenLocalhost(5001 + instanceId); -}); +builder.AddServiceDefaults(); +builder.AddKeyedAzureTableClient("clustering"); +builder.UseOrleans(); builder.Services.AddHostedService(); builder.Services.AddSignalR().AddJsonProtocol(); -WebApplication app = builder.Build(); +var app = builder.Build(); + + if (app.Environment.IsDevelopment()) { app.UseDeveloperExceptionPage(); } + app.UseStaticFiles(); -app.UseDefaultFiles(); app.UseRouting(); app.UseAuthorization(); app.MapHub("/locationHub"); -app.MapPrometheusScrapingEndpoint(); -app.Run(); +app.MapDefaultEndpoints(); +await app.RunAsync(); diff --git a/orleans/GPSTracker/GPSTracker.ServiceDefaults/Extensions.cs b/orleans/GPSTracker/GPSTracker.ServiceDefaults/Extensions.cs new file mode 100644 index 00000000000..08eb64cb6d9 --- /dev/null +++ b/orleans/GPSTracker/GPSTracker.ServiceDefaults/Extensions.cs @@ -0,0 +1,123 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Diagnostics.HealthChecks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Diagnostics.HealthChecks; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.ServiceDiscovery; +using OpenTelemetry; +using OpenTelemetry.Metrics; +using OpenTelemetry.Trace; + +namespace Microsoft.Extensions.Hosting; + +// Adds common .NET Aspire services: service discovery, resilience, health checks, and OpenTelemetry. +// This project should be referenced by each service project in your solution. +// To learn more about using this project, see https://aka.ms/dotnet/aspire/service-defaults +public static class Extensions +{ + public static TBuilder AddServiceDefaults(this TBuilder builder) where TBuilder : IHostApplicationBuilder + { + builder.ConfigureOpenTelemetry(); + + builder.AddDefaultHealthChecks(); + + builder.Services.AddServiceDiscovery(); + + builder.Services.ConfigureHttpClientDefaults(http => + { + // Turn on resilience by default + http.AddStandardResilienceHandler(); + + // Turn on service discovery by default + http.AddServiceDiscovery(); + }); + + // Uncomment the following to restrict the allowed schemes for service discovery. + // builder.Services.Configure(options => + // { + // options.AllowedSchemes = ["https"]; + // }); + + return builder; + } + + public static TBuilder ConfigureOpenTelemetry(this TBuilder builder) where TBuilder : IHostApplicationBuilder + { + builder.Logging.AddOpenTelemetry(logging => + { + logging.IncludeFormattedMessage = true; + logging.IncludeScopes = true; + }); + + builder.Services.AddOpenTelemetry() + .WithMetrics(metrics => + { + metrics.AddAspNetCoreInstrumentation() + .AddHttpClientInstrumentation() + .AddRuntimeInstrumentation() + .AddMeter("Microsoft.Orleans"); + }) + .WithTracing(tracing => + { + // Uncomment to see Orleans internal traces. Very noisy. + // tracing.AddSource("Microsoft.Orleans.Runtime"); + tracing.AddSource("Microsoft.Orleans.Application"); + tracing.AddSource(builder.Environment.ApplicationName) + .AddAspNetCoreInstrumentation() + // Uncomment the following line to enable gRPC instrumentation (requires the OpenTelemetry.Instrumentation.GrpcNetClient package) + //.AddGrpcClientInstrumentation() + .AddHttpClientInstrumentation(); + }); + + builder.AddOpenTelemetryExporters(); + + return builder; + } + + private static TBuilder AddOpenTelemetryExporters(this TBuilder builder) where TBuilder : IHostApplicationBuilder + { + var useOtlpExporter = !string.IsNullOrWhiteSpace(builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]); + + if (useOtlpExporter) + { + builder.Services.AddOpenTelemetry().UseOtlpExporter(); + } + + // Uncomment the following lines to enable the Azure Monitor exporter (requires the Azure.Monitor.OpenTelemetry.AspNetCore package) + //if (!string.IsNullOrEmpty(builder.Configuration["APPLICATIONINSIGHTS_CONNECTION_STRING"])) + //{ + // builder.Services.AddOpenTelemetry() + // .UseAzureMonitor(); + //} + + return builder; + } + + public static TBuilder AddDefaultHealthChecks(this TBuilder builder) where TBuilder : IHostApplicationBuilder + { + builder.Services.AddHealthChecks() + // Add a default liveness check to ensure app is responsive + .AddCheck("self", () => HealthCheckResult.Healthy(), ["live"]); + + return builder; + } + + public static WebApplication MapDefaultEndpoints(this WebApplication app) + { + // Adding health checks endpoints to applications in non-development environments has security implications. + // See https://aka.ms/dotnet/aspire/healthchecks for details before enabling these endpoints in non-development environments. + if (app.Environment.IsDevelopment()) + { + // All health checks must pass for app to be considered ready to accept traffic after starting + app.MapHealthChecks("/health"); + + // Only health checks tagged with the "live" tag must pass for app to be considered alive + app.MapHealthChecks("/alive", new HealthCheckOptions + { + Predicate = r => r.Tags.Contains("live") + }); + } + + return app; + } +} diff --git a/orleans/GPSTracker/GPSTracker.ServiceDefaults/GPSTracker.ServiceDefaults.csproj b/orleans/GPSTracker/GPSTracker.ServiceDefaults/GPSTracker.ServiceDefaults.csproj new file mode 100644 index 00000000000..f2dd6c96753 --- /dev/null +++ b/orleans/GPSTracker/GPSTracker.ServiceDefaults/GPSTracker.ServiceDefaults.csproj @@ -0,0 +1,22 @@ + + + + net9.0 + enable + enable + true + + + + + + + + + + + + + + + diff --git a/orleans/GPSTracker/GPSTracker.sln b/orleans/GPSTracker/GPSTracker.sln index 49a9b734b6e..93f60698606 100644 --- a/orleans/GPSTracker/GPSTracker.sln +++ b/orleans/GPSTracker/GPSTracker.sln @@ -21,6 +21,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution screenshot.jpeg = screenshot.jpeg EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GPSTracker.AppHost", "GPSTracker.AppHost\GPSTracker.AppHost.csproj", "{63BE60F4-5F4A-43B9-AB32-E9075AB17437}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GPSTracker.ServiceDefaults", "GPSTracker.ServiceDefaults\GPSTracker.ServiceDefaults.csproj", "{6776EAA8-7997-4C09-8765-5E4DB0AA2BFE}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -39,6 +43,14 @@ Global {B3ECE7C7-E076-4868-B6E1-9AFD16F8FA1F}.Debug|Any CPU.Build.0 = Debug|Any CPU {B3ECE7C7-E076-4868-B6E1-9AFD16F8FA1F}.Release|Any CPU.ActiveCfg = Release|Any CPU {B3ECE7C7-E076-4868-B6E1-9AFD16F8FA1F}.Release|Any CPU.Build.0 = Release|Any CPU + {63BE60F4-5F4A-43B9-AB32-E9075AB17437}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {63BE60F4-5F4A-43B9-AB32-E9075AB17437}.Debug|Any CPU.Build.0 = Debug|Any CPU + {63BE60F4-5F4A-43B9-AB32-E9075AB17437}.Release|Any CPU.ActiveCfg = Release|Any CPU + {63BE60F4-5F4A-43B9-AB32-E9075AB17437}.Release|Any CPU.Build.0 = Release|Any CPU + {6776EAA8-7997-4C09-8765-5E4DB0AA2BFE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6776EAA8-7997-4C09-8765-5E4DB0AA2BFE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6776EAA8-7997-4C09-8765-5E4DB0AA2BFE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6776EAA8-7997-4C09-8765-5E4DB0AA2BFE}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/orleans/GPSTracker/README.md b/orleans/GPSTracker/README.md index c69d69465ae..02e589cc85d 100644 --- a/orleans/GPSTracker/README.md +++ b/orleans/GPSTracker/README.md @@ -39,7 +39,7 @@ var notifier = GrainFactory.GetGrain(0); ## Sample prerequisites -This sample is written in C# and targets .NET 8.0. It requires the [.NET 8.0 SDK](https://dotnet.microsoft.com/download/dotnet/8.0) or later. +This sample is written in C# and targets .NET 9.0. It requires the [.NET 9.0 SDK](https://dotnet.microsoft.com/download/dotnet/9.0) or later. ## Building the sample @@ -54,42 +54,10 @@ To download and run the sample, follow these steps: 1. Navigate to the folder that holds the unzipped sample code. 2. At the command line, type [`dotnet run`](https://docs.microsoft.com/dotnet/core/tools/dotnet-run). -Open three terminal windows. In the first terminal window, run the following at the command prompt: +## Run the solution -```dotnetcli -dotnet run --project GPSTracker.Service -``` - -In the second terminal, launch another instance of the host, specifying that it's the second host by passing an `InstanceId` value as follows: - -```dotnetcli -dotnet run --project GPSTracker.Service -- --InstanceId 1 -``` +Start the `GPSTracker.AppHost` project in Visual Studio or from the command line: -Now open a web browser to `http://localhost:5001/index.html`. At this point, there will be no points moving around the map. - -In the third terminal window, run the following at the command prompt to begin simulating devices: - -```dotnetcli -dotnet run --project GPSTracker.FakeDeviceGateway ``` - -Dots will appear on the map in the Web browser and begin moving randomly around the area. - -## Orleans observability - -If you're interested in observability, this sample includes some optional logging, metrics, and distributed tracing. - -```docker -docker run -p 9090:9090 -v ..\dotnet\samples\GPSTracker\prometheus.yml:/etc/prometheus/prometheus.yml prom/prometheus -``` - -```docker -docker run -d --name jaeger -e COLLECTOR_ZIPKIN_HOST_PORT=:9411 -p 5775:5775/udp -p 6831:6831/udp -p 6832:6832/udp -p 5778:5778 -p 16686:16686 -p 14268:14268 -p 14250:14250 -p 9411:9411 jaegertracing/all-in-one:1.22 -``` - -OR zipkin: - -```docker -docker run -p 9411:9411 openzipkin/zipkin +dotnet run --project GPSTracker.AppHost ```