Skip to content

Instantly share code, notes, and snippets.

@ReubenBond
Last active June 6, 2024 13:04
Show Gist options
  • Save ReubenBond/2881c9f519910ca4a378d148346e9114 to your computer and use it in GitHub Desktop.
Save ReubenBond/2881c9f519910ca4a378d148346e9114 to your computer and use it in GitHub Desktop.
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Orleans.Concurrency;
using Orleans.Configuration;
using Orleans.Hosting;
using Orleans.Runtime;
namespace MyNamespace
{
class Program
{
static async Task Main(string[] args)
{
// Configure the silo host
var silo = new SiloHostBuilder()
// ... your silo config ...
.EnableDirectClient()
.Build();
// Start the silo
await silo.StartAsync();
var webHost = new WebHostBuilder()
.UseKestrel(options => options.ListenAnyIP(8880))
.ConfigureServices(
services =>
{
// Respond to health check probes.
services.AddHealthChecks()
.AddCheck<SiloHealthCheck>("SiloHealth")
.AddCheck<GrainHealthCheck>("GrainHealth");
// Add the required services from the silo.
services.AddSingleton(sp => silo.Services.GetRequiredService<IClusterClient>());
services.AddTransient(
sp => silo.Services.GetRequiredService<IEnumerable<IHealthCheckParticipant>>());
})
.UseStartup<Startup>()
.Build();
await webHost.StartAsync();
await Task.Delay(-1);
}
public interface ILocalHealthCheckGrain : IGrainWithGuidKey
{
Task Ping();
}
[StatelessWorker]
[CollectionAgeLimit(Minutes = 2)]
public class LocalHealthCheckGrain : Grain, ILocalHealthCheckGrain
{
public Task Ping() => Task.CompletedTask;
}
public class GrainHealthCheck : IHealthCheck
{
private readonly IClusterClient client;
public GrainHealthCheck(IClusterClient client)
{
this.client = client;
}
public async Task<HealthCheckResult> CheckHealthAsync(
HealthCheckContext context,
CancellationToken cancellationToken = new CancellationToken())
{
try
{
var localGrain = this.client.GetGrain<ILocalHealthCheckGrain>(Guid.Empty);
await localGrain.Ping();
}
catch (Exception exception)
{
return HealthCheckResult.Unhealthy("Unable to ping local health check grain", exception);
}
return HealthCheckResult.Healthy();
}
}
public class SiloHealthCheck : IHealthCheck
{
private DateTime lastCheckTime;
private readonly IEnumerable<IHealthCheckParticipant> participants;
public SiloHealthCheck(IEnumerable<IHealthCheckParticipant> participants)
{
this.participants = participants;
}
public Task<HealthCheckResult> CheckHealthAsync(
HealthCheckContext context,
CancellationToken cancellationToken = new CancellationToken())
{
var lastChecked = this.lastCheckTime;
this.lastCheckTime = DateTime.UtcNow;
foreach (var participant in this.participants)
{
if (!participant.CheckHealth(lastChecked))
{
return Task.FromResult(HealthCheckResult.Degraded());
}
}
return Task.FromResult(HealthCheckResult.Healthy());
}
}
public class Startup
{
public void Configure(IApplicationBuilder app)
{
app.UseHealthChecks("/healthz");
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment