Skip to content

Commit c8250ed

Browse files
EtherZazarusz
authored andcommitted
#251 Health check circuit breaker
Signed-off-by: Richard Pringle <richardpringle@gmail.com>
1 parent d04154f commit c8250ed

File tree

63 files changed

+2298
-421
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+2298
-421
lines changed

build/tasks.ps1

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ $projects = @(
4343
"SlimMessageBus.Host.Outbox.Sql",
4444
"SlimMessageBus.Host.Outbox.Sql.DbContext",
4545

46+
"SlimMessageBus.Host.CircuitBreaker.HealthCheck",
47+
4648
"SlimMessageBus.Host.AsyncApi"
4749
)
4850

docs/intro.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
- [Set message headers](#set-message-headers)
88
- [Consumer](#consumer)
99
- [Start or Stop message consumption](#start-or-stop-message-consumption)
10+
- [Health check circuit breaker](#health-check-circuit-breaker)
1011
- [Consumer context (additional message information)](#consumer-context-additional-message-information)
1112
- [Per-message DI container scope](#per-message-di-container-scope)
1213
- [Hybrid bus and message scope reuse](#hybrid-bus-and-message-scope-reuse)
@@ -291,6 +292,32 @@ await consumerControl.Stop();
291292

292293
> Since version 1.15.5
293294

295+
#### Health check circuit breaker
296+
297+
Consumers can be linked to [.NET app health checks](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/diagnostic-health-checks) [tags](https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/health-checks#register-health-check-services), enabling or disabling the consumer based on the health check status reported by the [Health Check Publisher](https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/health-checks#health-check-publisher). A consumer associated with one or more tags will only be active if all health checks linked to the tags are passing.
298+
299+
```cs
300+
// add health checks with tags
301+
builder.Services
302+
.AddHealthChecks()
303+
.AddCheck<StorageHealthCheck>("Storage", tags: ["Storage"]);
304+
.AddCheck<SqlServerHealthCheck>("SqlServer", tags: ["Sql"]);
305+
306+
builder.Services
307+
.AddSlimMessageBus(mbb => {
308+
...
309+
310+
mbb.Consume<Message>(cfg => {
311+
...
312+
313+
// configure consumer to monitor tag/state
314+
cfg.PauseOnUnhealthyCheck("Storage");
315+
cfg.PauseOnDegradedHealthCheck("Sql");
316+
})
317+
})
318+
```
319+
*Requires: SlimMessageBus.Host.CircuitBreaker.HealthCheck*
320+
294321
#### Consumer context (additional message information)
295322

296323
The consumer can access the [`IConsumerContext`](../src/SlimMessageBus/IConsumerContext.cs) object which:

docs/intro.t.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
- [Set message headers](#set-message-headers)
88
- [Consumer](#consumer)
99
- [Start or Stop message consumption](#start-or-stop-message-consumption)
10+
- [Health check circuit breaker](#health-check-circuit-breaker)
1011
- [Consumer context (additional message information)](#consumer-context-additional-message-information)
1112
- [Per-message DI container scope](#per-message-di-container-scope)
1213
- [Hybrid bus and message scope reuse](#hybrid-bus-and-message-scope-reuse)
@@ -291,6 +292,32 @@ await consumerControl.Stop();
291292

292293
> Since version 1.15.5
293294

295+
#### Health check circuit breaker
296+
297+
Consumers can be linked to [.NET app health checks](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/diagnostic-health-checks) [tags](https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/health-checks#register-health-check-services), enabling or disabling the consumer based on the health check status reported by the [Health Check Publisher](https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/health-checks#health-check-publisher). A consumer associated with one or more tags will only be active if all health checks linked to the tags are passing.
298+
299+
```cs
300+
// add health checks with tags
301+
builder.Services
302+
.AddHealthChecks()
303+
.AddCheck<StorageHealthCheck>("Storage", tags: ["Storage"]);
304+
.AddCheck<SqlServerHealthCheck>("SqlServer", tags: ["Sql"]);
305+
306+
builder.Services
307+
.AddSlimMessageBus(mbb => {
308+
...
309+
310+
mbb.Consume<Message>(cfg => {
311+
...
312+
313+
// configure consumer to monitor tag/state
314+
cfg.PauseOnUnhealthyCheck("Storage");
315+
cfg.PauseOnDegradedHealthCheck("Sql");
316+
})
317+
})
318+
```
319+
*Requires: SlimMessageBus.Host.CircuitBreaker.HealthCheck*
320+
294321
#### Consumer context (additional message information)
295322

296323
The consumer can access the [`IConsumerContext`](../src/SlimMessageBus/IConsumerContext.cs) object which:

src/.editorconfig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,11 +178,14 @@ dotnet_style_allow_multiple_blank_lines_experimental = true:silent
178178
dotnet_style_allow_statement_immediately_after_block_experimental = true:silent
179179
dotnet_style_prefer_collection_expression = when_types_loosely_match:suggestion
180180
dotnet_diagnostic.CA1859.severity = silent
181+
181182
dotnet_style_qualification_for_field = false:suggestion
182183
dotnet_style_qualification_for_property = false:suggestion
183184
dotnet_style_qualification_for_method = false:suggestion
184185
dotnet_style_qualification_for_event = false:suggestion
185186
dotnet_diagnostic.VSTHRD200.severity = none
187+
# not supported by .netstandard2.0
188+
dotnet_diagnostic.CA1510.severity = none
186189

187190
[*.{csproj,xml}]
188191
indent_style = space
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
namespace Sample.CircuitBreaker.HealthCheck.Consumers;
2+
3+
public class AddConsumer : IConsumer<Add>
4+
{
5+
private readonly ILogger<AddConsumer> _logger;
6+
7+
public AddConsumer(ILogger<AddConsumer> logger)
8+
{
9+
_logger = logger;
10+
}
11+
12+
public Task OnHandle(Add message, CancellationToken cancellationToken)
13+
{
14+
_logger.LogInformation("{A} + {B} = {C}", message.a, message.b, message.a + message.b);
15+
return Task.CompletedTask;
16+
}
17+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
namespace Sample.CircuitBreaker.HealthCheck.Consumers;
2+
3+
public class SubtractConsumer : IConsumer<Subtract>
4+
{
5+
private readonly ILogger<SubtractConsumer> _logger;
6+
7+
public SubtractConsumer(ILogger<SubtractConsumer> logger)
8+
{
9+
_logger = logger;
10+
}
11+
12+
public Task OnHandle(Subtract message, CancellationToken cancellationToken)
13+
{
14+
_logger.LogInformation("{A} - {B} = {C}", message.a, message.b, message.a - message.b);
15+
return Task.CompletedTask;
16+
}
17+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
global using System.Net.Mime;
2+
global using System.Reflection;
3+
4+
global using Microsoft.Extensions.Configuration;
5+
global using Microsoft.Extensions.DependencyInjection;
6+
global using Microsoft.Extensions.Hosting;
7+
global using Microsoft.Extensions.Logging;
8+
9+
global using Sample.CircuitBreaker.HealthCheck.Consumers;
10+
global using Sample.CircuitBreaker.HealthCheck.Models;
11+
12+
global using SecretStore;
13+
14+
global using SlimMessageBus;
15+
global using SlimMessageBus.Host;
16+
global using SlimMessageBus.Host.RabbitMQ;
17+
global using SlimMessageBus.Host.Serialization.SystemTextJson;
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
namespace Sample.CircuitBreaker.HealthCheck.HealthChecks;
2+
3+
using Microsoft.Extensions.Logging;
4+
5+
public class AddRandomHealthCheck : RandomHealthCheck
6+
{
7+
public AddRandomHealthCheck(ILogger<AddRandomHealthCheck> logger)
8+
: base(logger)
9+
{
10+
}
11+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
namespace Sample.CircuitBreaker.HealthCheck.HealthChecks;
2+
3+
using Microsoft.Extensions.Diagnostics.HealthChecks;
4+
5+
public abstract class RandomHealthCheck : IHealthCheck
6+
{
7+
private readonly ILogger _logger;
8+
9+
protected RandomHealthCheck(ILogger logger)
10+
{
11+
_logger = logger;
12+
}
13+
14+
public Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
15+
{
16+
var value = (HealthStatus)Random.Shared.Next(3);
17+
_logger.LogInformation("{HealthCheck} evaluated as {HealthStatus}", this.GetType(), value);
18+
return Task.FromResult(new HealthCheckResult(value, value.ToString()));
19+
}
20+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
namespace Sample.CircuitBreaker.HealthCheck.HealthChecks;
2+
3+
using Microsoft.Extensions.Logging;
4+
5+
public class SubtractRandomHealthCheck : RandomHealthCheck
6+
{
7+
public SubtractRandomHealthCheck(ILogger<SubtractRandomHealthCheck> logger)
8+
: base(logger)
9+
{
10+
}
11+
}

0 commit comments

Comments
 (0)