Meetup: Copenhangen .NET User Group
Location: Copenhagen, Denmark
Abstract:
* I don't feel so well… Integrating health checks in your .NET Core solutions *
Do you have any idea how your ASP.NET Web Apps and APIs are functioning? Are they behaving healthily or in a degraded state? You might be able to tell from log information and telemetry data, but why not have them tell you how healthy they are themselves? ASP.NET Core 2.2 introduces health endpoints that let your apps and APIs do just that. In this session you will learn how to make health checks an integral part of your solution. We will cover various types of health checks ranging from internal status, such as memory thresholds, to health based on external dependencies, such as databases and HTTP endpoints. Finally, you are going to see how this all can be used in a Docker container cluster to allow the orchestrator to check for liveliness and readiness based on your health endpoints.
* It depends: .NET Core dependency injection *
The dependency injection system of .NET Core is very elaborate and allows for complex inversion of control scenarios. You will see the ins and outs of doing that, but also learn how to avoid mistakes that might be easy to miss.
.NET Core comes with its own dependency injection system, that you probably know from ASP.NET Core. In this session we will have a detailed look at all of the specifics of Microsoft’s default DI system for .NET Core applications. You will learn how to properly use the Inversion of Control features, such as registering services, scopes, lifetimes, and how to resolve instances. Armed with this DI knowledge, we will revisit ASP.NET Core and investigate bootstrapping and typical scenarios for middleware, background processes and resolving objects from MVC and Razor. At the end there will be a deep dive into topics with service descriptors, implementation factories, do’s and don’ts and pitfalls to avoid memory leaks in your implementation.
New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
Health monitoring and dependency injection - CNUG November 2019
1. Using dependency injection and
health checks in your
.NET Core solutions
Alex Thissen
Cloud architect at Xpirit, The Netherlands
2. Keeping entire system running
Determine state of entire system and intervene
How to know health status of individual services?
Collecting/correlating performance and health data
Sentry.ioRaygun.io RunscopeNewRelic AlertSite DataDogAppMetrics Azure Monitor
3. Centralized
Challenging
Doctor, am I sick? 12:34
Let's look at your vitals we
measured earlier:
- Pulse 60 per minute
- Blood pressure 130/80
12:34
Looks fine to me.
It seems you are healthy. 12:35
Thanks, doctor! 12:36
Let's look at your vitals we
measured:
- Pulse 180 per minute
- Blood pressure 150/110
12:34
Does not look good.
It seems you are unhealthy. 12:35
4. Modern medicine and health
Self-assessment
Context matters
You know best
Let's see. My vitals say:
- Pulse 180 per minute
- Blood pressure 150/110 12:34
How are you doing today?
12:36
Does not look good.
It seems I am unhealthy. 12:34
Oops, we need to do
something about that! 3:24
Good to know.
Stay healthy! 12:36
It's okay, as I am working out now
My back does not hurt.
So, I'm healthy!
12:34
5. Difference between metrics and health info
Metrics
Many individual measured values and counts
of events
Watch performance and trends
Useful for diagnostics and troubleshooting
Logic external to origin
Health
Intrinsic knowledge of implementation
required
DevOps mindset:
Logic to determine health is part of origin
Deployed together, good for autonomy
7. Healthy
• 200 OK
• "Everything is fine"
Degraded
• 200 OK
• "Could be doing
better or about to
become unhealthy"
Unhealthy
• 503 Service
Unavailable
• "Not able to perform"
8. ASP.NET Core application
/api/v1/… Middle
ware
New in .NET Core 2.2
Bootstrap health checks in
ASP.NET Core app
services.AddHealthChecks();
endpoints.MapHealthChecks("/health);
/health DefaultHealthCheckService
Microsoft.Extensions.Diagnostics.HealthChecks
.Abstractions
.EntityFramework
Microsoft.AspNetCore.Diagnostics.HealthChecks
9. What?
When?
How?
When a service is
unhealthy, how can you
trust its health status?public interface IHealthCheck
{
Task<HealthCheckResult> CheckHealthAsync(
HealthCheckContext context,
CancellationToken cancellationToken = default);
}
From: https://github.com/aspnet/Diagnostics/blob/master/src/
Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions/HealthReport.cs
12. Only 1 out-of-box check
DbContext
Build your own
IHealthCheck
Community packages
Xabaril/BeatPulse
Yours?AspNetCore.Diagnostics.HealthChecks.*
Microsoft.Extensions.Diagnostics.
HealthChecks.EntityFrameworkCore
services.AddHealthChecks()
.AddDbContextCheck<GamingDbContext>("EF")
13. Register multiple health endpoints
Middleware options
Register custom health check as singleton
/api/v1/…
/health
/ping
services.AddSingleton<KafkaHealthCheck>());
services.AddSingleton(new SqlConnectionHealthCheck(
new SqlConnection(Configuration.GetConnectionString("TestDB"))));
14. 1. Customize health endpoint output for more details
2. Query endpoint(s)
3. Build user interface
17. Pushes out health
info periodically
Options
ASP.NET Core application
DefaultHealthCheckService
HealthCheckPublisher
HostedService
IEnumerable<IHealthCheckPublisher>
services.AddHealthChecks()
.AddApplicationInsightsPublisher()
.AddPrometheusGatewayPublisher(
"http://pushgateway:9091/metrics",
"pushgateway") IHealthCheckPublisher
18. Registering a HealthCheckPublisher pre-.NET Core 3.0
// The following workaround permits adding an IHealthCheckPublisher
// instance to the service container when one or more other hosted
// services have already been added to the app. This workaround
// won't be required with the release of ASP.NET Core 3.0. For more
// information, see: https://github.com/aspnet/Extensions/issues/639.
services.TryAddEnumerable(
ServiceDescriptor.Singleton(typeof(IHostedService),
typeof(HealthCheckPublisherOptions).Assembly
.GetType(HealthCheckServiceAssembly)));
26. Expose as little detail as possible
Use different internal port
Add authentication using
middleware
Publish instead of endpoint
app.UseWhen(
ctx => ctx.User.Identity.IsAuthenticated,
a => a.UseHealthChecks("/securehealth")
);
27. 1. Assume degraded state
2. Set short timeouts on checks
3. Avoid complicated health checks
4. Register health check as singletons in DI
29. 1. Unit testing
2. SOLID: Dependency
Inversion Principle
3. Loose coupling
4. Clean architecture
5. .NET Core is using it
Related
Domain
Logic
Presentation
From Jason Taylor: Clean Architecture with ASP.NET Core 2.2
31. Possible ways to map
Try Add<ServiceType ImplementationType>
Builder pattern
Add TryAdd
ServiceType ImplementationType
32. Demo: ASP.NET Core DI
Quick look at how you were using
dependency injection all along
33. Register
• How
• Add… and TryAdd… of
type mappings
• Add… extension
methods
• Where
• Application root
• Startup class (ASP.NET)
Resolve
• Implicit
• Constructor injection
• ASP.NET Core specifics
• Explicit
• GetService<T>
• GetRequiredService<T>
• Also for enumerables
Release
• Automatic
• Resolved instances and
dependencies
• Scopes
• End of scope
• Might need to create
child scopes
IServiceCollection IServiceProvider IDisposable
1 2 3
34. Singleton
• Easy way to
implement
singleton pattern
• One instance per
DI container
• Beware of
concurrency and
threading issues
Scoped
• Duration of scope
• ASP.NET uses web request as scope
• Useful for UnitOfWork objects,
e.g. DbContext
• See also ServiceScopeFactory
Transient
• Single use
• Most common and safest
35. Typical startup in ASP.NET Core
public class Startup
{
public Startup(IConfiguration configuration, IHostEnvironment env)
public void ConfigureServices(IServiceCollection services)
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, …)
}
builder.UseStartup<Startup>();
1
2
3
36. Many ways to inject a
service instance
IServiceProvider
Pro tip
ActivatorUtilities
IServiceProvider
Register using
Configure(Services)
injection in Startup
class
Constructor
injection
[FromServices]
attribute
@inject
IApplicationBuilder.
ApplicationServices
HttpContext.
RequestServices
Middleware
Constructor or
Invoke method
37. Demo: Resolving services
in ASP.NET Core
Controller constructors
@inject
FromServicesAttribute
Application- and RequestServices
38. Hosting outside
and without ASP.NET Core
IHostedService
BackgroundService
Uses new Host, and
IHostBuilder for hosting
Startup
WebHostBuilder
HostBuilder Configure
Microsoft.Extensions.Hosting
Microsoft.Extensions.DependencyInjection
39. Resolved instances are released at end of scope
IDisposable Dispose
Create your own scopes with ServiceScopeFactory
Especially important for hosted services
AddHostedService<T>
using (var scope = serviceScopeFactory.CreateScope()) {
ISomeRepository scoped =
scope.ServiceProvider.GetRequiredService<ISomeRepository>();
await DoMyWork(scoped);
}
44. Plug in your favorite DI Framework
Option 1: While building host
UseServiceProviderFactory
ConfigureContainer
Option 2: When configuring services
ConfigureServices
IServiceProvider
Some alternative
DI Frameworks
Autofac
Castle Windsor
Lamar
LightInject
Ninject
SimpleInjector
Spring.NET
Unity
46. Different registrations for environments
Configure{Environment}Services
Configure{Environment}
Startup{Environment} StartupProduction
Requires different startup
UseStartup<Startup>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder => {
var assembly = typeof(Startup).GetTypeInfo().Assembly;
webBuilder.UseStartup(assemblyName.GetName().Name);
});
47. Demo: Tips and tricks
Convention based startup
TestServer services override
48. Avoid use Service Locator pattern
IServiceProvider
IServiceScopeFactory.CreateScope
Do not directly inject HttpContext
IHttpContextAccessor
accessor.HttpContext.RequestServices
Avoid complex lifetime graphs