diff --git a/src/HealthChecks.Rabbitmq.v6/DependencyInjection/RabbitMQHealthCheckBuilderExtensions.cs b/src/HealthChecks.Rabbitmq.v6/DependencyInjection/RabbitMQHealthCheckBuilderExtensions.cs
new file mode 100644
index 0000000000..3b4b1ad61b
--- /dev/null
+++ b/src/HealthChecks.Rabbitmq.v6/DependencyInjection/RabbitMQHealthCheckBuilderExtensions.cs
@@ -0,0 +1,207 @@
+using HealthChecks.RabbitMQ;
+using Microsoft.Extensions.Diagnostics.HealthChecks;
+using RabbitMQ.Client;
+
+namespace Microsoft.Extensions.DependencyInjection;
+
+///
+/// Extension methods to configure .
+///
+public static class RabbitMQHealthCheckBuilderExtensions
+{
+ private const string NAME = "rabbitmq";
+
+ ///
+ /// Add a health check for RabbitMQ services using connection string (amqp uri).
+ ///
+ /// The .
+ /// The RabbitMQ connection string to be used.
+ /// The RabbitMQ ssl options. Optional. If null, the ssl option will counted as disabled and not used.
+ /// The health check name. Optional. If null the type name 'rabbitmq' will be used for the name.
+ ///
+ /// The that should be reported when the health check fails. Optional. If null then
+ /// the default status of will be reported.
+ ///
+ /// A list of tags that can be used to filter sets of health checks. Optional.
+ /// An optional representing the timeout of the check.
+ /// The specified .
+ public static IHealthChecksBuilder AddRabbitMQ(
+ this IHealthChecksBuilder builder,
+ string rabbitConnectionString,
+ SslOption? sslOption = default,
+ string? name = default,
+ HealthStatus? failureStatus = default,
+ IEnumerable? tags = default,
+ TimeSpan? timeout = default)
+ {
+ return builder.AddRabbitMQ(new Uri(rabbitConnectionString), sslOption, name, failureStatus, tags, timeout);
+ }
+
+ ///
+ /// Add a health check for RabbitMQ services using connection string (amqp uri).
+ ///
+ /// The .
+ /// The RabbitMQ connection string to be used.
+ /// The RabbitMQ ssl options. Optional. If null, the ssl option will counted as disabled and not used.
+ /// The health check name. Optional. If null the type name 'rabbitmq' will be used for the name.
+ ///
+ /// The that should be reported when the health check fails. Optional. If null then
+ /// the default status of will be reported.
+ ///
+ /// A list of tags that can be used to filter sets of health checks. Optional.
+ /// An optional representing the timeout of the check.
+ /// The specified .
+ public static IHealthChecksBuilder AddRabbitMQ(
+ this IHealthChecksBuilder builder,
+ Uri rabbitConnectionString,
+ SslOption? sslOption = default,
+ string? name = default,
+ HealthStatus? failureStatus = default,
+ IEnumerable? tags = default,
+ TimeSpan? timeout = default)
+ {
+ var options = new RabbitMQHealthCheckOptions
+ {
+ ConnectionUri = rabbitConnectionString,
+ Ssl = sslOption
+ };
+
+ return builder.Add(new HealthCheckRegistration(
+ name ?? NAME,
+ new RabbitMQHealthCheck(options),
+ failureStatus,
+ tags,
+ timeout));
+ }
+
+ ///
+ /// Add a health check for RabbitMQ services using from service provider
+ /// or from service provider if none is found. At least one must be configured.
+ ///
+ ///
+ /// This method shouldn't be called more than once.
+ /// Each subsequent call will create a new connection, which overrides the previous ones.
+ ///
+ /// The .
+ /// The health check name. Optional. If null the type name 'rabbitmq' will be used for the name.
+ ///
+ /// The that should be reported when the health check fails. Optional. If null then
+ /// the default status of will be reported.
+ ///
+ /// A list of tags that can be used to filter sets of health checks. Optional.
+ /// An optional representing the timeout of the check.
+ /// The specified .
+ public static IHealthChecksBuilder AddRabbitMQ(
+ this IHealthChecksBuilder builder,
+ string? name = default,
+ HealthStatus? failureStatus = default,
+ IEnumerable? tags = default,
+ TimeSpan? timeout = default)
+ {
+ builder.Services.AddSingleton(sp =>
+ {
+ var connection = sp.GetService();
+ var connectionFactory = sp.GetService();
+
+ if (connection != null)
+ {
+ return new RabbitMQHealthCheck(new RabbitMQHealthCheckOptions { Connection = connection });
+ }
+ else if (connectionFactory != null)
+ {
+ return new RabbitMQHealthCheck(new RabbitMQHealthCheckOptions { ConnectionFactory = connectionFactory });
+ }
+ else
+ {
+ throw new ArgumentException($"Either an IConnection or IConnectionFactory must be registered with the service provider");
+ }
+ });
+
+ return builder.Add(new HealthCheckRegistration(
+ name ?? NAME,
+ sp => sp.GetRequiredService(),
+ failureStatus,
+ tags,
+ timeout));
+ }
+
+ ///
+ /// Add a health check for RabbitMQ services.
+ ///
+ ///
+ /// will be called each time the healthcheck route is requested. However
+ /// the created will be reused.
+ ///
+ /// The .
+ /// The action to configure the RabbitMQ setup.
+ /// The health check name. Optional. If null the type name 'rabbitmq' will be used for the name.
+ ///
+ /// The that should be reported when the health check fails. Optional. If null then
+ /// the default status of will be reported.
+ ///
+ /// A list of tags that can be used to filter sets of health checks. Optional.
+ /// An optional representing the timeout of the check.
+ /// The specified .
+ public static IHealthChecksBuilder AddRabbitMQ(
+ this IHealthChecksBuilder builder,
+ Action? setup,
+ string? name = default,
+ HealthStatus? failureStatus = default,
+ IEnumerable? tags = default,
+ TimeSpan? timeout = default)
+ {
+ var options = new RabbitMQHealthCheckOptions();
+ setup?.Invoke(options);
+
+ return builder.Add(new HealthCheckRegistration(
+ name ?? NAME,
+ new RabbitMQHealthCheck(options),
+ failureStatus,
+ tags,
+ timeout));
+ }
+
+ ///
+ /// Add a health check for RabbitMQ services.
+ ///
+ ///
+ /// will be called the first time the healthcheck route is requested. However
+ /// the created will be reused.
+ ///
+ /// The .
+ /// The action to configure the RabbitMQ setup with .
+ /// The health check name. Optional. If null the type name 'rabbitmq' will be used for the name.
+ ///
+ /// The that should be reported when the health check fails. Optional. If null then
+ /// the default status of will be reported.
+ ///
+ /// A list of tags that can be used to filter sets of health checks. Optional.
+ /// An optional representing the timeout of the check.
+ /// The specified .
+ public static IHealthChecksBuilder AddRabbitMQ(
+ this IHealthChecksBuilder builder,
+ Action? setup,
+ string? name = default,
+ HealthStatus? failureStatus = default,
+ IEnumerable? tags = default,
+ TimeSpan? timeout = default)
+ {
+ var options = new RabbitMQHealthCheckOptions();
+
+ return builder.Add(new HealthCheckRegistration(
+ name ?? NAME,
+ sp =>
+ {
+ if (!options.AlreadyConfiguredByHealthCheckRegistrationCall)
+ {
+ setup?.Invoke(sp, options);
+ options.AlreadyConfiguredByHealthCheckRegistrationCall = true;
+ }
+
+ return new RabbitMQHealthCheck(options);
+ },
+ failureStatus,
+ tags,
+ timeout));
+ }
+}
diff --git a/src/HealthChecks.Rabbitmq.v6/HealthChecks.Rabbitmq.v6.csproj b/src/HealthChecks.Rabbitmq.v6/HealthChecks.Rabbitmq.v6.csproj
index de95041214..84982f03a2 100644
--- a/src/HealthChecks.Rabbitmq.v6/HealthChecks.Rabbitmq.v6.csproj
+++ b/src/HealthChecks.Rabbitmq.v6/HealthChecks.Rabbitmq.v6.csproj
@@ -14,10 +14,6 @@
-
-
-
-
diff --git a/src/HealthChecks.Rabbitmq.v6/README.md b/src/HealthChecks.Rabbitmq.v6/README.md
new file mode 100644
index 0000000000..d95a46adbd
--- /dev/null
+++ b/src/HealthChecks.Rabbitmq.v6/README.md
@@ -0,0 +1,91 @@
+# RabbitMQ Health Check
+
+This health check verifies the ability to communicate with a RabbitMQ server
+
+## Example Usage
+
+With all of the following examples, you can additionally add the following parameters:
+
+- `name`: The health check name. Default if not specified is `rabbitmq`.
+- `failureStatus`: The `HealthStatus` that should be reported when the health check fails. Default is `HealthStatus.Unhealthy`.
+- `tags`: A list of tags that can be used to filter sets of health checks.
+- `timeout`: A `System.TimeSpan` representing the timeout of the check.
+
+### Basic
+
+This will create a new `IConnection` and reuse on every request to get the health check result. Use
+the extension method where you provide the `Uri` to connect with. You can optionally set the `SslOption` if needed.
+IConnection created with this option use UseBackgroundThreadsForIO by default in order to gracefully shutdown on non reference IConnection by ServiceCollection.
+
+```csharp
+public void ConfigureServices(IServiceCollection services)
+{
+ services
+ .AddHealthChecks()
+ .AddRabbitMQ(rabbitConnectionString: "amqps://user:pass@host1/vhost")
+ .AddRabbitMQ(rabbitConnectionString: "amqps://user:pass@host2/vhost");
+}
+```
+
+### Dependency Injected `IConnection`
+
+As per [RabbitMQ docs](https://www.rabbitmq.com/connections.html) and its suggestions on
+[high connectivity churn](https://www.rabbitmq.com/networking.html#dealing-with-high-connection-churn), connections are meant to be long lived.
+Ideally, this should be configured as a singleton.
+
+If you are sharing a single connection for every time a health check is requested,
+you must ensure automatic recovery is enable so that the connection can be re-established if lost.
+
+```csharp
+public void ConfigureServices(IServiceCollection services)
+{
+ services
+ .AddSingleton(sp =>
+ {
+ var factory = new ConnectionFactory
+ {
+ Uri = new Uri("amqps://user:pass@host/vhost"),
+ AutomaticRecoveryEnabled = true
+ };
+ return factory.CreateConnection();
+ })
+ .AddHealthChecks()
+ .AddRabbitMQ();
+}
+```
+
+Alternatively, you can specify the connection to use with a factory function given the `IServiceProvider`.
+
+```csharp
+public void ConfigureServices(IServiceCollection services)
+{
+ services
+ .AddHealthChecks()
+ .AddRabbitMQ(sp =>
+ {
+ var factory = new ConnectionFactory
+ {
+ Uri = new Uri("amqps://user:pass@host/vhost"),
+ AutomaticRecoveryEnabled = true
+ };
+ return factory.CreateConnection();
+ });
+}
+```
+
+Or you register IConnectionFactory and then the healthcheck will create a single connection for that one.
+
+```csharp
+public void ConfigureServices(IServiceCollection services)
+{
+ services
+ .AddSingleton(sp =>
+ new ConnectionFactory
+ {
+ Uri = new Uri("amqps://user:pass@host/vhost"),
+ AutomaticRecoveryEnabled = true
+ })
+ .AddHealthChecks()
+ .AddRabbitMQ();
+}
+```
diff --git a/src/HealthChecks.Rabbitmq/RabbitMQHealthCheckOptions.cs b/src/HealthChecks.Rabbitmq.v6/RabbitMQHealthCheckOptions.cs
similarity index 100%
rename from src/HealthChecks.Rabbitmq/RabbitMQHealthCheckOptions.cs
rename to src/HealthChecks.Rabbitmq.v6/RabbitMQHealthCheckOptions.cs
diff --git a/src/HealthChecks.Rabbitmq/DependencyInjection/RabbitMQHealthCheckBuilderExtensions.cs b/src/HealthChecks.Rabbitmq/DependencyInjection/RabbitMQHealthCheckBuilderExtensions.cs
index 2ad58dec9c..9082222c2b 100644
--- a/src/HealthChecks.Rabbitmq/DependencyInjection/RabbitMQHealthCheckBuilderExtensions.cs
+++ b/src/HealthChecks.Rabbitmq/DependencyInjection/RabbitMQHealthCheckBuilderExtensions.cs
@@ -11,129 +11,14 @@ public static class RabbitMQHealthCheckBuilderExtensions
{
private const string NAME = "rabbitmq";
- ///
- /// Add a health check for RabbitMQ services using connection string (amqp uri).
- ///
- /// The .
- /// The RabbitMQ connection string to be used.
- /// The RabbitMQ ssl options. Optional. If null, the ssl option will counted as disabled and not used.
- /// The health check name. Optional. If null the type name 'rabbitmq' will be used for the name.
- ///
- /// The that should be reported when the health check fails. Optional. If null then
- /// the default status of will be reported.
- ///
- /// A list of tags that can be used to filter sets of health checks. Optional.
- /// An optional representing the timeout of the check.
- /// The specified .
- public static IHealthChecksBuilder AddRabbitMQ(
- this IHealthChecksBuilder builder,
- string rabbitConnectionString,
- SslOption? sslOption = default,
- string? name = default,
- HealthStatus? failureStatus = default,
- IEnumerable? tags = default,
- TimeSpan? timeout = default)
- {
- return builder.AddRabbitMQ(new Uri(rabbitConnectionString), sslOption, name, failureStatus, tags, timeout);
- }
-
- ///
- /// Add a health check for RabbitMQ services using connection string (amqp uri).
- ///
- /// The .
- /// The RabbitMQ connection string to be used.
- /// The RabbitMQ ssl options. Optional. If null, the ssl option will counted as disabled and not used.
- /// The health check name. Optional. If null the type name 'rabbitmq' will be used for the name.
- ///
- /// The that should be reported when the health check fails. Optional. If null then
- /// the default status of will be reported.
- ///
- /// A list of tags that can be used to filter sets of health checks. Optional.
- /// An optional representing the timeout of the check.
- /// The specified .
- public static IHealthChecksBuilder AddRabbitMQ(
- this IHealthChecksBuilder builder,
- Uri rabbitConnectionString,
- SslOption? sslOption = default,
- string? name = default,
- HealthStatus? failureStatus = default,
- IEnumerable? tags = default,
- TimeSpan? timeout = default)
- {
- var options = new RabbitMQHealthCheckOptions
- {
- ConnectionUri = rabbitConnectionString,
- Ssl = sslOption
- };
-
- return builder.Add(new HealthCheckRegistration(
- name ?? NAME,
- new RabbitMQHealthCheck(options),
- failureStatus,
- tags,
- timeout));
- }
-
- ///
- /// Add a health check for RabbitMQ services using from service provider
- /// or from service provider if none is found. At least one must be configured.
- ///
- ///
- /// This method shouldn't be called more than once.
- /// Each subsequent call will create a new connection, which overrides the previous ones.
- ///
- /// The .
- /// The health check name. Optional. If null the type name 'rabbitmq' will be used for the name.
- ///
- /// The that should be reported when the health check fails. Optional. If null then
- /// the default status of will be reported.
- ///
- /// A list of tags that can be used to filter sets of health checks. Optional.
- /// An optional representing the timeout of the check.
- /// The specified .
- public static IHealthChecksBuilder AddRabbitMQ(
- this IHealthChecksBuilder builder,
- string? name = default,
- HealthStatus? failureStatus = default,
- IEnumerable? tags = default,
- TimeSpan? timeout = default)
- {
- builder.Services.AddSingleton(sp =>
- {
- var connection = sp.GetService();
- var connectionFactory = sp.GetService();
-
- if (connection != null)
- {
- return new RabbitMQHealthCheck(new RabbitMQHealthCheckOptions { Connection = connection });
- }
- else if (connectionFactory != null)
- {
- return new RabbitMQHealthCheck(new RabbitMQHealthCheckOptions { ConnectionFactory = connectionFactory });
- }
- else
- {
- throw new ArgumentException($"Either an IConnection or IConnectionFactory must be registered with the service provider");
- }
- });
-
- return builder.Add(new HealthCheckRegistration(
- name ?? NAME,
- sp => sp.GetRequiredService(),
- failureStatus,
- tags,
- timeout));
- }
-
///
/// Add a health check for RabbitMQ services.
///
///
- /// will be called each time the healthcheck route is requested. However
- /// the created will be reused.
+ /// will be called each time the healthcheck route is requested.
///
/// The .
- /// The action to configure the RabbitMQ setup.
+ /// The optional factory method to get the RabbitMQ connection from the .
/// The health check name. Optional. If null the type name 'rabbitmq' will be used for the name.
///
/// The that should be reported when the health check fails. Optional. If null then
@@ -144,32 +29,38 @@ public static IHealthChecksBuilder AddRabbitMQ(
/// The specified .
public static IHealthChecksBuilder AddRabbitMQ(
this IHealthChecksBuilder builder,
- Action? setup,
+ Func? factory = null,
string? name = default,
HealthStatus? failureStatus = default,
IEnumerable? tags = default,
TimeSpan? timeout = default)
{
- var options = new RabbitMQHealthCheckOptions();
- setup?.Invoke(options);
-
return builder.Add(new HealthCheckRegistration(
name ?? NAME,
- new RabbitMQHealthCheck(options),
+ sp =>
+ {
+ return new RabbitMQHealthCheck(sp, sp =>
+ {
+ // Pass the factory to RabbitMQHealthCheck to ensure the factory is called
+ // within CheckHealthAsync. HealthCheckRegistration.Factory should not throw
+ // exceptions, which can happen in the factory or getting the service.
+ var connection = factory?.Invoke(sp) ?? sp.GetRequiredService();
+ return Task.FromResult(connection);
+ });
+ },
failureStatus,
tags,
timeout));
}
///
- /// Add a health check for RabbitMQ services.
+ /// Add a health check for RabbitMQ services allowing for an asynchronous connection factory.
///
///
- /// will be called each time the healthcheck route is requested. However
- /// the created will be reused.
+ /// will be called each time the healthcheck route is requested.
///
/// The .
- /// The action to configure the RabbitMQ setup with .
+ /// The asynchronous factory method to get the RabbitMQ connection from the .
/// The health check name. Optional. If null the type name 'rabbitmq' will be used for the name.
///
/// The that should be reported when the health check fails. Optional. If null then
@@ -180,26 +71,15 @@ public static IHealthChecksBuilder AddRabbitMQ(
/// The specified .
public static IHealthChecksBuilder AddRabbitMQ(
this IHealthChecksBuilder builder,
- Action? setup,
+ Func> factory,
string? name = default,
HealthStatus? failureStatus = default,
IEnumerable? tags = default,
TimeSpan? timeout = default)
{
- var options = new RabbitMQHealthCheckOptions();
-
return builder.Add(new HealthCheckRegistration(
name ?? NAME,
- sp =>
- {
- if (!options.AlreadyConfiguredByHealthCheckRegistrationCall)
- {
- setup?.Invoke(sp, options);
- options.AlreadyConfiguredByHealthCheckRegistrationCall = true;
- }
-
- return new RabbitMQHealthCheck(options);
- },
+ sp => new RabbitMQHealthCheck(sp, factory),
failureStatus,
tags,
timeout));
diff --git a/src/HealthChecks.Rabbitmq/HealthChecks.Rabbitmq.csproj b/src/HealthChecks.Rabbitmq/HealthChecks.Rabbitmq.csproj
index 2d2837aabb..cb31621a95 100644
--- a/src/HealthChecks.Rabbitmq/HealthChecks.Rabbitmq.csproj
+++ b/src/HealthChecks.Rabbitmq/HealthChecks.Rabbitmq.csproj
@@ -10,7 +10,6 @@
-
diff --git a/src/HealthChecks.Rabbitmq/README.md b/src/HealthChecks.Rabbitmq/README.md
index d95a46adbd..29ca0e2e5d 100644
--- a/src/HealthChecks.Rabbitmq/README.md
+++ b/src/HealthChecks.Rabbitmq/README.md
@@ -11,30 +11,11 @@ With all of the following examples, you can additionally add the following param
- `tags`: A list of tags that can be used to filter sets of health checks.
- `timeout`: A `System.TimeSpan` representing the timeout of the check.
-### Basic
-
-This will create a new `IConnection` and reuse on every request to get the health check result. Use
-the extension method where you provide the `Uri` to connect with. You can optionally set the `SslOption` if needed.
-IConnection created with this option use UseBackgroundThreadsForIO by default in order to gracefully shutdown on non reference IConnection by ServiceCollection.
-
-```csharp
-public void ConfigureServices(IServiceCollection services)
-{
- services
- .AddHealthChecks()
- .AddRabbitMQ(rabbitConnectionString: "amqps://user:pass@host1/vhost")
- .AddRabbitMQ(rabbitConnectionString: "amqps://user:pass@host2/vhost");
-}
-```
-
### Dependency Injected `IConnection`
As per [RabbitMQ docs](https://www.rabbitmq.com/connections.html) and its suggestions on
[high connectivity churn](https://www.rabbitmq.com/networking.html#dealing-with-high-connection-churn), connections are meant to be long lived.
-Ideally, this should be configured as a singleton.
-
-If you are sharing a single connection for every time a health check is requested,
-you must ensure automatic recovery is enable so that the connection can be re-established if lost.
+Ideally, this should be configured as a singleton. The health check should use the same IConnection instance that is used in the application.
```csharp
public void ConfigureServices(IServiceCollection services)
@@ -45,47 +26,38 @@ public void ConfigureServices(IServiceCollection services)
var factory = new ConnectionFactory
{
Uri = new Uri("amqps://user:pass@host/vhost"),
- AutomaticRecoveryEnabled = true
};
- return factory.CreateConnection();
+ return factory.CreateConnectionAsync().GetAwaiter().GetResult();
})
.AddHealthChecks()
.AddRabbitMQ();
}
```
-Alternatively, you can specify the connection to use with a factory function given the `IServiceProvider`.
+### Caching IConnection outside of Dependency Injection
+
+Alternatively, you can create the connection outside of the dependency injection container and use it in the health check.
```csharp
public void ConfigureServices(IServiceCollection services)
{
services
.AddHealthChecks()
- .AddRabbitMQ(sp =>
- {
- var factory = new ConnectionFactory
- {
- Uri = new Uri("amqps://user:pass@host/vhost"),
- AutomaticRecoveryEnabled = true
- };
- return factory.CreateConnection();
- });
+ .AddRabbitMQ(sp => connectionTask.Value);
}
-```
-Or you register IConnectionFactory and then the healthcheck will create a single connection for that one.
+private static readonly Lazy> connectionTask = new Lazy>(CreateConnection);
-```csharp
-public void ConfigureServices(IServiceCollection services)
+private static async Task CreateConnection()
{
- services
- .AddSingleton(sp =>
- new ConnectionFactory
- {
- Uri = new Uri("amqps://user:pass@host/vhost"),
- AutomaticRecoveryEnabled = true
- })
- .AddHealthChecks()
- .AddRabbitMQ();
+ var factory = new ConnectionFactory
+ {
+ Uri = new Uri("amqps://user:pass@host/vhost"),
+ };
+ return await factory.CreateConnectionAsync();
}
```
+
+### Breaking changes
+
+`RabbitMQHealthCheck` was letting the users specify how `IConnection` should be created (from raw connection string or from `Uri` or from `IConnectionFactory`), at a cost of maintaining an internal, static client instances cache. Now the type does not create client instances nor maintain an internal cache and **it's the caller responsibility to provide the instance of `IConnection`** (please see [#2048](https://github.com/Xabaril/AspNetCore.Diagnostics.HealthChecks/issues/2148) for more details). Since RabbitMQ [recommends](https://www.rabbitmq.com/client-libraries/dotnet-api-guide#connection-and-channel-lifespan) reusing client instances since they can be expensive to create, it's recommended to register a singleton factory method for `IConnection`. So the client is created only when needed and once per whole application lifetime.
diff --git a/src/HealthChecks.Rabbitmq/RabbitMQHealthCheck.cs b/src/HealthChecks.Rabbitmq/RabbitMQHealthCheck.cs
index 4de8e996f8..348a693160 100644
--- a/src/HealthChecks.Rabbitmq/RabbitMQHealthCheck.cs
+++ b/src/HealthChecks.Rabbitmq/RabbitMQHealthCheck.cs
@@ -1,4 +1,3 @@
-using System.Collections.Concurrent;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using RabbitMQ.Client;
@@ -9,20 +8,19 @@ namespace HealthChecks.RabbitMQ;
///
public class RabbitMQHealthCheck : IHealthCheck
{
- private static readonly ConcurrentDictionary> _connections = new();
+ private readonly IConnection? _connection;
+ private readonly IServiceProvider? _serviceProvider;
+ private readonly Func>? _factory;
- private IConnection? _connection;
- private readonly RabbitMQHealthCheckOptions _options;
-
- public RabbitMQHealthCheck(RabbitMQHealthCheckOptions options)
+ public RabbitMQHealthCheck(IConnection connection)
{
- _options = Guard.ThrowIfNull(options);
- _connection = options.Connection;
+ _connection = Guard.ThrowIfNull(connection);
+ }
- if (_connection is null && _options.ConnectionFactory is null && _options.ConnectionUri is null)
- {
- throw new ArgumentException("A connection, connection factory, or connection string must be set!", nameof(options));
- }
+ public RabbitMQHealthCheck(IServiceProvider serviceProvider, Func> factory)
+ {
+ _serviceProvider = Guard.ThrowIfNull(serviceProvider);
+ _factory = Guard.ThrowIfNull(factory);
}
///
@@ -30,7 +28,8 @@ public async Task CheckHealthAsync(HealthCheckContext context
{
try
{
- var connection = await EnsureConnectionAsync(cancellationToken).ConfigureAwait(false);
+ var connection = _connection ?? await _factory!(_serviceProvider!).ConfigureAwait(false);
+
await using var model = await connection.CreateChannelAsync(cancellationToken: cancellationToken).ConfigureAwait(false);
return HealthCheckResult.Healthy();
@@ -40,74 +39,4 @@ public async Task CheckHealthAsync(HealthCheckContext context
return new HealthCheckResult(context.Registration.FailureStatus, exception: ex);
}
}
-
- private async Task EnsureConnectionAsync(CancellationToken cancellationToken) =>
- _connection ??= await _connections.GetOrAddAsync(_options, async options =>
- {
- var factory = options.ConnectionFactory;
-
- if (factory is null)
- {
- Guard.ThrowIfNull(options.ConnectionUri);
- factory = new ConnectionFactory
- {
- Uri = options.ConnectionUri,
- AutomaticRecoveryEnabled = true
- };
-
- if (options.RequestedConnectionTimeout is not null)
- {
- ((ConnectionFactory)factory).RequestedConnectionTimeout = options.RequestedConnectionTimeout.Value;
- }
-
- if (options.Ssl is not null)
- {
- ((ConnectionFactory)factory).Ssl = options.Ssl;
- }
- }
-
- return await factory.CreateConnectionAsync(cancellationToken).ConfigureAwait(false);
- }).ConfigureAwait(false);
-}
-
-internal static class ConcurrentDictionaryExtensions
-{
- ///
- /// Provides an alternative to specifically for asynchronous values. The factory method will only run once.
- ///
- public static async Task GetOrAddAsync(
- this ConcurrentDictionary> dictionary,
- TKey key,
- Func> valueFactory) where TKey : notnull
- {
- while (true)
- {
- if (dictionary.TryGetValue(key, out var task))
- {
- return await task.ConfigureAwait(false);
- }
-
- // This is the task that we'll return to all waiters. We'll complete it when the factory is complete
- var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
- if (dictionary.TryAdd(key, tcs.Task))
- {
- try
- {
- var value = await valueFactory(key).ConfigureAwait(false);
- tcs.TrySetResult(value);
- return await tcs.Task.ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- // Make sure all waiters see the exception
- tcs.SetException(ex);
-
- // We remove the entry if the factory failed so it's not a permanent failure
- // and future gets can retry (this could be a pluggable policy)
- dictionary.TryRemove(key, out _);
- throw;
- }
- }
- }
- }
}
diff --git a/test/HealthChecks.RabbitMQ.Tests/DependencyInjection/RegistrationTests.cs b/test/HealthChecks.RabbitMQ.Tests/DependencyInjection/RegistrationTests.cs
index bec2f371b2..435919b6af 100644
--- a/test/HealthChecks.RabbitMQ.Tests/DependencyInjection/RegistrationTests.cs
+++ b/test/HealthChecks.RabbitMQ.Tests/DependencyInjection/RegistrationTests.cs
@@ -1,3 +1,5 @@
+using RabbitMQ.Client;
+
namespace HealthChecks.RabbitMQ.Tests.DependencyInjection;
public class rabbitmq_registration_should
@@ -10,7 +12,7 @@ public void add_health_check_when_properly_configured()
{
var services = new ServiceCollection();
services.AddHealthChecks()
- .AddRabbitMQ(rabbitConnectionString: FAKE_CONNECTION_STRING);
+ .AddRabbitMQ();
using var serviceProvider = services.BuildServiceProvider();
var options = serviceProvider.GetRequiredService>();
@@ -29,7 +31,7 @@ public void add_named_health_check_when_properly_configured()
var customCheckName = "my-" + DEFAULT_CHECK_NAME;
services.AddHealthChecks()
- .AddRabbitMQ(FAKE_CONNECTION_STRING, name: customCheckName);
+ .AddRabbitMQ(name: customCheckName);
using var serviceProvider = services.BuildServiceProvider();
var options = serviceProvider.GetRequiredService>();
@@ -52,39 +54,11 @@ public void add_named_health_check_with_connection_string_factory_by_iServicePro
});
services.AddHealthChecks()
- .AddRabbitMQ((sp, options) =>
+ .AddRabbitMQ(sp =>
{
var connectionString = sp.GetRequiredService().ConnectionString;
- options.ConnectionUri = new Uri(connectionString);
- }, name: customCheckName);
-
- using var serviceProvider = services.BuildServiceProvider();
- var options = serviceProvider.GetRequiredService>();
-
- var registration = options.Value.Registrations.First();
- var check = registration.Factory(serviceProvider);
-
- registration.Name.ShouldBe(customCheckName);
- check.ShouldBeOfType();
- }
-
- [Fact]
- public void add_named_health_check_with_uri_string_factory_by_iServiceProvider_registered()
- {
- var services = new ServiceCollection();
- var customCheckName = "my-" + DEFAULT_CHECK_NAME;
- services.AddSingleton(new RabbitMqSetting
- {
- ConnectionString = FAKE_CONNECTION_STRING
- });
-
- services.AddHealthChecks()
- .AddRabbitMQ((sp, options) =>
- {
- var connectionString = new Uri(sp.GetRequiredService().ConnectionString);
-
- options.ConnectionUri = connectionString;
+ return new ConnectionFactory() { Uri = new Uri(connectionString) }.CreateConnectionAsync();
}, name: customCheckName);
using var serviceProvider = services.BuildServiceProvider();
diff --git a/test/HealthChecks.RabbitMQ.Tests/Functional/RabbitHealthCheckTests.cs b/test/HealthChecks.RabbitMQ.Tests/Functional/RabbitHealthCheckTests.cs
index 7a8aafb209..b0886b62e7 100644
--- a/test/HealthChecks.RabbitMQ.Tests/Functional/RabbitHealthCheckTests.cs
+++ b/test/HealthChecks.RabbitMQ.Tests/Functional/RabbitHealthCheckTests.cs
@@ -14,33 +14,9 @@ public async Task be_healthy_if_rabbitmq_is_available()
.ConfigureServices(services =>
{
services.AddHealthChecks()
- .AddRabbitMQ(rabbitConnectionString: connectionString, tags: ["rabbitmq"]);
- })
- .Configure(app =>
- {
- app.UseHealthChecks("/health", new HealthCheckOptions
- {
- Predicate = r => r.Tags.Contains("rabbitmq")
- });
- });
-
- using var server = new TestServer(webHostBuilder);
-
- using var response = await server.CreateRequest("/health").GetAsync();
-
- response.StatusCode.ShouldBe(HttpStatusCode.OK);
- }
-
- [Fact]
- public async Task be_healthy_if_rabbitmq_is_available_using_ssloption()
- {
- var connectionString = "amqp://localhost:5672";
-
- var webHostBuilder = new WebHostBuilder()
- .ConfigureServices(services =>
- {
- services.AddHealthChecks()
- .AddRabbitMQ(rabbitConnectionString: connectionString, sslOption: new SslOption(serverName: "localhost", enabled: false), tags: ["rabbitmq"]);
+ .AddRabbitMQ(
+ _ => new ConnectionFactory() { Uri = new Uri(connectionString) }.CreateConnectionAsync(),
+ tags: ["rabbitmq"]);
})
.Configure(app =>
{
@@ -60,11 +36,15 @@ public async Task be_healthy_if_rabbitmq_is_available_using_ssloption()
[Fact]
public async Task be_unhealthy_if_rabbitmq_is_not_available()
{
+ var connectionString = "amqp://localhost:6672";
+
var webHostBuilder = new WebHostBuilder()
.ConfigureServices(services =>
{
services.AddHealthChecks()
- .AddRabbitMQ("amqp://localhost:6672", sslOption: new SslOption(serverName: "localhost", enabled: false), tags: ["rabbitmq"]);
+ .AddRabbitMQ(
+ _ => new ConnectionFactory() { Uri = new Uri(connectionString) }.CreateConnectionAsync(),
+ tags: ["rabbitmq"]);
})
.Configure(app =>
{
@@ -82,7 +62,7 @@ public async Task be_unhealthy_if_rabbitmq_is_not_available()
}
[Fact]
- public async Task be_healthy_if_rabbitmq_is_available_using_iconnectionfactory()
+ public async Task be_healthy_if_rabbitmq_is_available_using_iconnection()
{
var connectionString = "amqp://localhost:5672";
@@ -93,12 +73,18 @@ public async Task be_healthy_if_rabbitmq_is_available_using_iconnectionfactory()
Ssl = new SslOption(serverName: "localhost", enabled: false)
};
+ var connection = await factory.CreateConnectionAsync();
+
var webHostBuilder = new WebHostBuilder()
.ConfigureServices(services =>
{
services
.AddHealthChecks()
- .AddRabbitMQ(options => options.ConnectionFactory = factory, tags: ["rabbitmq"]);
+ .Add(new HealthCheckRegistration(
+ "rabbitmq",
+ _ => new RabbitMQHealthCheck(connection),
+ failureStatus: null,
+ tags: ["rabbitmq"]));
})
.Configure(app =>
{
@@ -116,7 +102,7 @@ public async Task be_healthy_if_rabbitmq_is_available_using_iconnectionfactory()
}
[Fact]
- public async Task be_healthy_if_rabbitmq_is_available_using_iconnection()
+ public async Task be_healthy_if_rabbitmq_is_available_using_iconnection_in_serviceprovider()
{
var connectionString = "amqp://localhost:5672";
@@ -127,11 +113,7 @@ public async Task be_healthy_if_rabbitmq_is_available_using_iconnection()
Ssl = new SslOption(serverName: "localhost", enabled: false)
};
-#if RABBITMQ_V6
- var connection = factory.CreateConnection();
-#else
var connection = await factory.CreateConnectionAsync();
-#endif
var webHostBuilder = new WebHostBuilder()
.ConfigureServices(services =>
@@ -156,32 +138,6 @@ public async Task be_healthy_if_rabbitmq_is_available_using_iconnection()
response.StatusCode.ShouldBe(HttpStatusCode.OK);
}
- [Fact]
- public async Task be_healthy_if_rabbitmq_is_available_and_specify_default_ssloption()
- {
- var connectionString = "amqp://localhost:5672";
-
- var webHostBuilder = new WebHostBuilder()
- .ConfigureServices(services =>
- {
- services.AddHealthChecks()
- .AddRabbitMQ(connectionString, sslOption: new SslOption(serverName: "localhost", enabled: false), tags: ["rabbitmq"]);
- })
- .Configure(app =>
- {
- app.UseHealthChecks("/health", new HealthCheckOptions
- {
- Predicate = r => r.Tags.Contains("rabbitmq")
- });
- });
-
- using var server = new TestServer(webHostBuilder);
-
- using var response = await server.CreateRequest("/health").GetAsync();
-
- response.StatusCode.ShouldBe(HttpStatusCode.OK);
- }
-
[Fact]
public async Task be_not_crash_on_startup_when_rabbitmq_is_down_at_startup()
{
@@ -199,7 +155,9 @@ public async Task be_not_crash_on_startup_when_rabbitmq_is_down_at_startup()
};
})
.AddHealthChecks()
- .AddRabbitMQ(tags: ["rabbitmq"]);
+ .AddRabbitMQ(
+ sp => sp.GetRequiredService().CreateConnectionAsync(),
+ tags: ["rabbitmq"]);
})
.Configure(app =>
{
@@ -215,34 +173,6 @@ public async Task be_not_crash_on_startup_when_rabbitmq_is_down_at_startup()
response1.StatusCode.ShouldBe(HttpStatusCode.ServiceUnavailable);
}
- [Fact]
- public async Task be_healthy_if_rabbitmq_is_available_using_iServiceProvider()
- {
- var connectionString = "amqp://localhost:5672";
-
- var webHostBuilder = new WebHostBuilder()
- .ConfigureServices(services =>
- {
- services
- .AddHealthChecks()
- .AddRabbitMQ(options => options.ConnectionUri = new Uri(connectionString), tags: ["rabbitmq"]);
-
- })
- .Configure(app =>
- {
- app.UseHealthChecks("/health", new HealthCheckOptions
- {
- Predicate = r => r.Tags.Contains("rabbitmq")
- });
- });
-
- using var server = new TestServer(webHostBuilder);
-
- using var response = await server.CreateRequest("/health").GetAsync();
-
- response.StatusCode.ShouldBe(HttpStatusCode.OK);
- }
-
[Fact]
public async Task two_rabbitmq_health_check()
{
@@ -252,9 +182,12 @@ public async Task two_rabbitmq_health_check()
var webHostBuilder = new WebHostBuilder()
.ConfigureServices(services =>
{
+ services.AddKeyedSingleton("1", (sp, _) => new ConnectionFactory() { Uri = new Uri(connectionString1) }.CreateConnectionAsync().GetAwaiter().GetResult());
+ services.AddKeyedSingleton("2", (sp, _) => new ConnectionFactory() { Uri = new Uri(connectionString2) }.CreateConnectionAsync().GetAwaiter().GetResult());
+
services.AddHealthChecks()
- .AddRabbitMQ(rabbitConnectionString: connectionString1, name: "rabbitmq1")
- .AddRabbitMQ(rabbitConnectionString: connectionString2, name: "rabbitmq2");
+ .AddRabbitMQ(sp => sp.GetRequiredKeyedService("1"), name: "rabbitmq1")
+ .AddRabbitMQ(sp => sp.GetRequiredKeyedService("2"), name: "rabbitmq2");
})
.Configure(app =>
{
@@ -277,29 +210,27 @@ public async Task two_rabbitmq_health_check()
response2.StatusCode.ShouldBe(HttpStatusCode.ServiceUnavailable);
}
- // https://github.com/Xabaril/AspNetCore.Diagnostics.HealthChecks/issues/714
[Fact]
- public async Task should_respect_timeout()
+ public async Task no_connection_registered()
{
- var services = new ServiceCollection();
-
- services
- .AddLogging()
- .AddHealthChecks()
- .AddRabbitMQ(opt =>
+ var webHostBuilder = new WebHostBuilder()
+ .ConfigureServices(services =>
+ {
+ services
+ .AddHealthChecks()
+ .AddRabbitMQ(tags: ["rabbitmq"]);
+ })
+ .Configure(app =>
+ {
+ app.UseHealthChecks("/health", new HealthCheckOptions
{
- opt.RequestedConnectionTimeout = TimeSpan.FromSeconds(1);
- opt.ConnectionUri = new Uri($"amqps://user:pwd@invalid-host:5672");
- },
- timeout: TimeSpan.FromSeconds(10));
-
- using var provider = services.BuildServiceProvider();
- var healthCheckService = provider.GetRequiredService();
- var start = DateTime.Now;
- using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10));
- var report = await healthCheckService.CheckHealthAsync(cts.Token);
- report.Status.ShouldBe(HealthStatus.Unhealthy);
- var end = DateTime.Now;
- (end - start).ShouldBeLessThan(TimeSpan.FromSeconds(10));
+ Predicate = r => r.Tags.Contains("rabbitmq")
+ });
+ });
+
+ using var server = new TestServer(webHostBuilder);
+
+ using var response1 = await server.CreateRequest("/health").GetAsync();
+ response1.StatusCode.ShouldBe(HttpStatusCode.ServiceUnavailable);
}
}
diff --git a/test/HealthChecks.RabbitMQ.Tests/HealthChecks.Rabbitmq.approved.txt b/test/HealthChecks.RabbitMQ.Tests/HealthChecks.Rabbitmq.approved.txt
index e4e522964f..c0f129fb20 100644
--- a/test/HealthChecks.RabbitMQ.Tests/HealthChecks.Rabbitmq.approved.txt
+++ b/test/HealthChecks.RabbitMQ.Tests/HealthChecks.Rabbitmq.approved.txt
@@ -2,27 +2,16 @@ namespace HealthChecks.RabbitMQ
{
public class RabbitMQHealthCheck : Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck
{
- public RabbitMQHealthCheck(HealthChecks.RabbitMQ.RabbitMQHealthCheckOptions options) { }
+ public RabbitMQHealthCheck(RabbitMQ.Client.IConnection connection) { }
+ public RabbitMQHealthCheck(System.IServiceProvider serviceProvider, System.Func> factory) { }
public System.Threading.Tasks.Task CheckHealthAsync(Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckContext context, System.Threading.CancellationToken cancellationToken = default) { }
}
- public class RabbitMQHealthCheckOptions
- {
- public RabbitMQHealthCheckOptions() { }
- public RabbitMQ.Client.IConnection? Connection { get; set; }
- public RabbitMQ.Client.IConnectionFactory? ConnectionFactory { get; set; }
- public System.Uri? ConnectionUri { get; set; }
- public System.TimeSpan? RequestedConnectionTimeout { get; set; }
- public RabbitMQ.Client.SslOption? Ssl { get; set; }
- }
}
namespace Microsoft.Extensions.DependencyInjection
{
public static class RabbitMQHealthCheckBuilderExtensions
{
- public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddRabbitMQ(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string? name = null, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus = default, System.Collections.Generic.IEnumerable? tags = null, System.TimeSpan? timeout = default) { }
- public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddRabbitMQ(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, System.Action? setup, string? name = null, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus = default, System.Collections.Generic.IEnumerable? tags = null, System.TimeSpan? timeout = default) { }
- public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddRabbitMQ(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, System.Action? setup, string? name = null, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus = default, System.Collections.Generic.IEnumerable? tags = null, System.TimeSpan? timeout = default) { }
- public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddRabbitMQ(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string rabbitConnectionString, RabbitMQ.Client.SslOption? sslOption = null, string? name = null, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus = default, System.Collections.Generic.IEnumerable? tags = null, System.TimeSpan? timeout = default) { }
- public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddRabbitMQ(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, System.Uri rabbitConnectionString, RabbitMQ.Client.SslOption? sslOption = null, string? name = null, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus = default, System.Collections.Generic.IEnumerable? tags = null, System.TimeSpan? timeout = default) { }
+ public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddRabbitMQ(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, System.Func> factory, string? name = null, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus = default, System.Collections.Generic.IEnumerable? tags = null, System.TimeSpan? timeout = default) { }
+ public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddRabbitMQ(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, System.Func? factory = null, string? name = null, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus = default, System.Collections.Generic.IEnumerable? tags = null, System.TimeSpan? timeout = default) { }
}
}
\ No newline at end of file
diff --git a/test/HealthChecks.RabbitMQ.v6.Tests/DependencyInjection/RegistrationTests.cs b/test/HealthChecks.RabbitMQ.v6.Tests/DependencyInjection/RegistrationTests.cs
new file mode 100644
index 0000000000..6ceaa458f6
--- /dev/null
+++ b/test/HealthChecks.RabbitMQ.v6.Tests/DependencyInjection/RegistrationTests.cs
@@ -0,0 +1,104 @@
+namespace HealthChecks.RabbitMQ.Tests.DependencyInjection;
+
+public class rabbitmq_registration_should
+{
+ private const string FAKE_CONNECTION_STRING = "amqp://server";
+ private const string DEFAULT_CHECK_NAME = "rabbitmq";
+
+ [Fact]
+ public void add_health_check_when_properly_configured()
+ {
+ var services = new ServiceCollection();
+ services.AddHealthChecks()
+ .AddRabbitMQ(rabbitConnectionString: FAKE_CONNECTION_STRING);
+
+ using var serviceProvider = services.BuildServiceProvider();
+ var options = serviceProvider.GetRequiredService>();
+
+ var registration = options.Value.Registrations.First();
+ var check = registration.Factory(serviceProvider);
+
+ registration.Name.ShouldBe(DEFAULT_CHECK_NAME);
+ check.ShouldBeOfType();
+ }
+
+ [Fact]
+ public void add_named_health_check_when_properly_configured()
+ {
+ var services = new ServiceCollection();
+ var customCheckName = "my-" + DEFAULT_CHECK_NAME;
+
+ services.AddHealthChecks()
+ .AddRabbitMQ(FAKE_CONNECTION_STRING, name: customCheckName);
+
+ using var serviceProvider = services.BuildServiceProvider();
+ var options = serviceProvider.GetRequiredService>();
+
+ var registration = options.Value.Registrations.First();
+ var check = registration.Factory(serviceProvider);
+
+ registration.Name.ShouldBe(customCheckName);
+ check.ShouldBeOfType();
+ }
+
+ [Fact]
+ public void add_named_health_check_with_connection_string_factory_by_iServiceProvider_registered()
+ {
+ var services = new ServiceCollection();
+ var customCheckName = "my-" + DEFAULT_CHECK_NAME;
+ services.AddSingleton(new RabbitMqSetting
+ {
+ ConnectionString = FAKE_CONNECTION_STRING
+ });
+
+ services.AddHealthChecks()
+ .AddRabbitMQ((sp, options) =>
+ {
+ var connectionString = sp.GetRequiredService().ConnectionString;
+
+ options.ConnectionUri = new Uri(connectionString);
+ }, name: customCheckName);
+
+ using var serviceProvider = services.BuildServiceProvider();
+ var options = serviceProvider.GetRequiredService>();
+
+ var registration = options.Value.Registrations.First();
+ var check = registration.Factory(serviceProvider);
+
+ registration.Name.ShouldBe(customCheckName);
+ check.ShouldBeOfType();
+ }
+
+ [Fact]
+ public void add_named_health_check_with_uri_string_factory_by_iServiceProvider_registered()
+ {
+ var services = new ServiceCollection();
+ var customCheckName = "my-" + DEFAULT_CHECK_NAME;
+ services.AddSingleton(new RabbitMqSetting
+ {
+ ConnectionString = FAKE_CONNECTION_STRING
+ });
+
+ services.AddHealthChecks()
+ .AddRabbitMQ((sp, options) =>
+ {
+ var connectionString = new Uri(sp.GetRequiredService().ConnectionString);
+
+ options.ConnectionUri = connectionString;
+ }, name: customCheckName);
+
+ using var serviceProvider = services.BuildServiceProvider();
+ var options = serviceProvider.GetRequiredService>();
+
+ var registration = options.Value.Registrations.First();
+ var check = registration.Factory(serviceProvider);
+
+ registration.Name.ShouldBe(customCheckName);
+ check.ShouldBeOfType();
+ }
+}
+
+public class RabbitMqSetting
+{
+ public string ConnectionString { get; set; } = null!;
+}
diff --git a/test/HealthChecks.RabbitMQ.v6.Tests/Functional/RabbitHealthCheckTests.cs b/test/HealthChecks.RabbitMQ.v6.Tests/Functional/RabbitHealthCheckTests.cs
new file mode 100644
index 0000000000..fc36601b36
--- /dev/null
+++ b/test/HealthChecks.RabbitMQ.v6.Tests/Functional/RabbitHealthCheckTests.cs
@@ -0,0 +1,301 @@
+using System.Net;
+using RabbitMQ.Client;
+
+namespace HealthChecks.RabbitMQ.Tests.Functional;
+
+public class rabbitmq_healthcheck_should
+{
+ [Fact]
+ public async Task be_healthy_if_rabbitmq_is_available()
+ {
+ var connectionString = "amqp://localhost:5672";
+
+ var webHostBuilder = new WebHostBuilder()
+ .ConfigureServices(services =>
+ {
+ services.AddHealthChecks()
+ .AddRabbitMQ(rabbitConnectionString: connectionString, tags: ["rabbitmq"]);
+ })
+ .Configure(app =>
+ {
+ app.UseHealthChecks("/health", new HealthCheckOptions
+ {
+ Predicate = r => r.Tags.Contains("rabbitmq")
+ });
+ });
+
+ using var server = new TestServer(webHostBuilder);
+
+ using var response = await server.CreateRequest("/health").GetAsync();
+
+ response.StatusCode.ShouldBe(HttpStatusCode.OK);
+ }
+
+ [Fact]
+ public async Task be_healthy_if_rabbitmq_is_available_using_ssloption()
+ {
+ var connectionString = "amqp://localhost:5672";
+
+ var webHostBuilder = new WebHostBuilder()
+ .ConfigureServices(services =>
+ {
+ services.AddHealthChecks()
+ .AddRabbitMQ(rabbitConnectionString: connectionString, sslOption: new SslOption(serverName: "localhost", enabled: false), tags: ["rabbitmq"]);
+ })
+ .Configure(app =>
+ {
+ app.UseHealthChecks("/health", new HealthCheckOptions
+ {
+ Predicate = r => r.Tags.Contains("rabbitmq")
+ });
+ });
+
+ using var server = new TestServer(webHostBuilder);
+
+ using var response = await server.CreateRequest("/health").GetAsync();
+
+ response.StatusCode.ShouldBe(HttpStatusCode.OK);
+ }
+
+ [Fact]
+ public async Task be_unhealthy_if_rabbitmq_is_not_available()
+ {
+ var webHostBuilder = new WebHostBuilder()
+ .ConfigureServices(services =>
+ {
+ services.AddHealthChecks()
+ .AddRabbitMQ("amqp://localhost:6672", sslOption: new SslOption(serverName: "localhost", enabled: false), tags: ["rabbitmq"]);
+ })
+ .Configure(app =>
+ {
+ app.UseHealthChecks("/health", new HealthCheckOptions
+ {
+ Predicate = r => r.Tags.Contains("rabbitmq")
+ });
+ });
+
+ using var server = new TestServer(webHostBuilder);
+
+ using var response = await server.CreateRequest("/health").GetAsync();
+
+ response.StatusCode.ShouldBe(HttpStatusCode.ServiceUnavailable);
+ }
+
+ [Fact]
+ public async Task be_healthy_if_rabbitmq_is_available_using_iconnectionfactory()
+ {
+ var connectionString = "amqp://localhost:5672";
+
+ var factory = new ConnectionFactory()
+ {
+ Uri = new Uri(connectionString),
+ AutomaticRecoveryEnabled = true,
+ Ssl = new SslOption(serverName: "localhost", enabled: false)
+ };
+
+ var webHostBuilder = new WebHostBuilder()
+ .ConfigureServices(services =>
+ {
+ services
+ .AddHealthChecks()
+ .AddRabbitMQ(options => options.ConnectionFactory = factory, tags: ["rabbitmq"]);
+ })
+ .Configure(app =>
+ {
+ app.UseHealthChecks("/health", new HealthCheckOptions
+ {
+ Predicate = r => r.Tags.Contains("rabbitmq")
+ });
+ });
+
+ using var server = new TestServer(webHostBuilder);
+
+ using var response = await server.CreateRequest("/health").GetAsync();
+
+ response.StatusCode.ShouldBe(HttpStatusCode.OK);
+ }
+
+ [Fact]
+ public async Task be_healthy_if_rabbitmq_is_available_using_iconnection()
+ {
+ var connectionString = "amqp://localhost:5672";
+
+ var factory = new ConnectionFactory()
+ {
+ Uri = new Uri(connectionString),
+ AutomaticRecoveryEnabled = true,
+ Ssl = new SslOption(serverName: "localhost", enabled: false)
+ };
+
+ var connection = factory.CreateConnection();
+
+ var webHostBuilder = new WebHostBuilder()
+ .ConfigureServices(services =>
+ {
+ services
+ .AddSingleton(connection)
+ .AddHealthChecks()
+ .AddRabbitMQ(tags: ["rabbitmq"]);
+ })
+ .Configure(app =>
+ {
+ app.UseHealthChecks("/health", new HealthCheckOptions
+ {
+ Predicate = r => r.Tags.Contains("rabbitmq")
+ });
+ });
+
+ using var server = new TestServer(webHostBuilder);
+
+ using var response = await server.CreateRequest("/health").GetAsync();
+
+ response.StatusCode.ShouldBe(HttpStatusCode.OK);
+ }
+
+ [Fact]
+ public async Task be_healthy_if_rabbitmq_is_available_and_specify_default_ssloption()
+ {
+ var connectionString = "amqp://localhost:5672";
+
+ var webHostBuilder = new WebHostBuilder()
+ .ConfigureServices(services =>
+ {
+ services.AddHealthChecks()
+ .AddRabbitMQ(connectionString, sslOption: new SslOption(serverName: "localhost", enabled: false), tags: ["rabbitmq"]);
+ })
+ .Configure(app =>
+ {
+ app.UseHealthChecks("/health", new HealthCheckOptions
+ {
+ Predicate = r => r.Tags.Contains("rabbitmq")
+ });
+ });
+
+ using var server = new TestServer(webHostBuilder);
+
+ using var response = await server.CreateRequest("/health").GetAsync();
+
+ response.StatusCode.ShouldBe(HttpStatusCode.OK);
+ }
+
+ [Fact]
+ public async Task be_not_crash_on_startup_when_rabbitmq_is_down_at_startup()
+ {
+ var webHostBuilder = new WebHostBuilder()
+ .ConfigureServices(services =>
+ {
+ services
+ .AddSingleton(sp =>
+ {
+ return new ConnectionFactory()
+ {
+ Uri = new Uri("amqp://localhost:3333"),
+ AutomaticRecoveryEnabled = true,
+ Ssl = new SslOption(serverName: "localhost", enabled: false)
+ };
+ })
+ .AddHealthChecks()
+ .AddRabbitMQ(tags: ["rabbitmq"]);
+ })
+ .Configure(app =>
+ {
+ app.UseHealthChecks("/health", new HealthCheckOptions
+ {
+ Predicate = r => r.Tags.Contains("rabbitmq")
+ });
+ });
+
+ using var server = new TestServer(webHostBuilder);
+
+ using var response1 = await server.CreateRequest("/health").GetAsync();
+ response1.StatusCode.ShouldBe(HttpStatusCode.ServiceUnavailable);
+ }
+
+ [Fact]
+ public async Task be_healthy_if_rabbitmq_is_available_using_iServiceProvider()
+ {
+ var connectionString = "amqp://localhost:5672";
+
+ var webHostBuilder = new WebHostBuilder()
+ .ConfigureServices(services =>
+ {
+ services
+ .AddHealthChecks()
+ .AddRabbitMQ(options => options.ConnectionUri = new Uri(connectionString), tags: ["rabbitmq"]);
+
+ })
+ .Configure(app =>
+ {
+ app.UseHealthChecks("/health", new HealthCheckOptions
+ {
+ Predicate = r => r.Tags.Contains("rabbitmq")
+ });
+ });
+
+ using var server = new TestServer(webHostBuilder);
+
+ using var response = await server.CreateRequest("/health").GetAsync();
+
+ response.StatusCode.ShouldBe(HttpStatusCode.OK);
+ }
+
+ [Fact]
+ public async Task two_rabbitmq_health_check()
+ {
+ const string connectionString1 = "amqp://localhost:5672";
+ const string connectionString2 = "amqp://localhost:6672/";
+
+ var webHostBuilder = new WebHostBuilder()
+ .ConfigureServices(services =>
+ {
+ services.AddHealthChecks()
+ .AddRabbitMQ(rabbitConnectionString: connectionString1, name: "rabbitmq1")
+ .AddRabbitMQ(rabbitConnectionString: connectionString2, name: "rabbitmq2");
+ })
+ .Configure(app =>
+ {
+ app.UseHealthChecks("/health1", new HealthCheckOptions
+ {
+ Predicate = r => r.Name.Equals("rabbitmq1")
+ });
+ app.UseHealthChecks("/health2", new HealthCheckOptions
+ {
+ Predicate = r => r.Name.Equals("rabbitmq2")
+ });
+ });
+
+ using var server = new TestServer(webHostBuilder);
+
+ using var response1 = await server.CreateRequest("/health1").GetAsync();
+ using var response2 = await server.CreateRequest("/health2").GetAsync();
+
+ response1.StatusCode.ShouldBe(HttpStatusCode.OK);
+ response2.StatusCode.ShouldBe(HttpStatusCode.ServiceUnavailable);
+ }
+
+ // https://github.com/Xabaril/AspNetCore.Diagnostics.HealthChecks/issues/714
+ [Fact]
+ public async Task should_respect_timeout()
+ {
+ var services = new ServiceCollection();
+
+ services
+ .AddLogging()
+ .AddHealthChecks()
+ .AddRabbitMQ(opt =>
+ {
+ opt.RequestedConnectionTimeout = TimeSpan.FromSeconds(1);
+ opt.ConnectionUri = new Uri($"amqps://user:pwd@invalid-host:5672");
+ },
+ timeout: TimeSpan.FromSeconds(10));
+
+ using var provider = services.BuildServiceProvider();
+ var healthCheckService = provider.GetRequiredService();
+ var start = DateTime.Now;
+ using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10));
+ var report = await healthCheckService.CheckHealthAsync(cts.Token);
+ report.Status.ShouldBe(HealthStatus.Unhealthy);
+ var end = DateTime.Now;
+ (end - start).ShouldBeLessThan(TimeSpan.FromSeconds(10));
+ }
+}
diff --git a/test/HealthChecks.RabbitMQ.v6.Tests/HealthChecks.RabbitMQ.v6.Tests.csproj b/test/HealthChecks.RabbitMQ.v6.Tests/HealthChecks.RabbitMQ.v6.Tests.csproj
index e0ec3cf27d..d37d8c713e 100644
--- a/test/HealthChecks.RabbitMQ.v6.Tests/HealthChecks.RabbitMQ.v6.Tests.csproj
+++ b/test/HealthChecks.RabbitMQ.v6.Tests/HealthChecks.RabbitMQ.v6.Tests.csproj
@@ -2,14 +2,10 @@
HealthChecks.RabbitMQ.Tests
- $(DefineConstants);RABBITMQ_V6
-
-
-