diff --git a/AspNetCore.Diagnostics.HealthChecks.sln b/AspNetCore.Diagnostics.HealthChecks.sln
index 56d827accc..8075587267 100644
--- a/AspNetCore.Diagnostics.HealthChecks.sln
+++ b/AspNetCore.Diagnostics.HealthChecks.sln
@@ -310,6 +310,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HealthChecks.Milvus", "src\
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HealthChecks.Milvus.Tests", "test\HealthChecks.Milvus.Tests\HealthChecks.Milvus.Tests.csproj", "{D49CF52C-9D21-4D98-8A15-A2B259E9C003}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HealthChecks.Rabbitmq.v7", "src\HealthChecks.Rabbitmq.v7\HealthChecks.Rabbitmq.v7.csproj", "{C76D7349-A3D2-7277-93C6-EE92E8E447A5}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HealthChecks.RabbitMQ.v7.Tests", "test\HealthChecks.RabbitMQ.v7.Tests\HealthChecks.RabbitMQ.v7.Tests.csproj", "{2787F63E-ABEA-9461-CDF3-97FE7C5C3DCC}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -868,6 +872,14 @@ Global
{D49CF52C-9D21-4D98-8A15-A2B259E9C003}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D49CF52C-9D21-4D98-8A15-A2B259E9C003}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D49CF52C-9D21-4D98-8A15-A2B259E9C003}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C76D7349-A3D2-7277-93C6-EE92E8E447A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C76D7349-A3D2-7277-93C6-EE92E8E447A5}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C76D7349-A3D2-7277-93C6-EE92E8E447A5}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C76D7349-A3D2-7277-93C6-EE92E8E447A5}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2787F63E-ABEA-9461-CDF3-97FE7C5C3DCC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2787F63E-ABEA-9461-CDF3-97FE7C5C3DCC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2787F63E-ABEA-9461-CDF3-97FE7C5C3DCC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2787F63E-ABEA-9461-CDF3-97FE7C5C3DCC}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -1011,6 +1023,8 @@ Global
{3B812989-2C4E-4FCE-B3A0-EF9C00A9B3A5} = {FF4414C2-8863-4ADA-8A1D-4B9F25C361FE}
{17913EAF-3B12-495B-80EA-9EB975FBE6BA} = {2A3FD988-2BB8-43CF-B3A2-B70E648259D4}
{D49CF52C-9D21-4D98-8A15-A2B259E9C003} = {FF4414C2-8863-4ADA-8A1D-4B9F25C361FE}
+ {C76D7349-A3D2-7277-93C6-EE92E8E447A5} = {2A3FD988-2BB8-43CF-B3A2-B70E648259D4}
+ {2787F63E-ABEA-9461-CDF3-97FE7C5C3DCC} = {FF4414C2-8863-4ADA-8A1D-4B9F25C361FE}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {2B8C62A1-11B6-469F-874C-A02443256568}
diff --git a/src/HealthChecks.Rabbitmq.v7/HealthChecks.Rabbitmq.v7.csproj b/src/HealthChecks.Rabbitmq.v7/HealthChecks.Rabbitmq.v7.csproj
new file mode 100644
index 0000000000..86bff9b5ab
--- /dev/null
+++ b/src/HealthChecks.Rabbitmq.v7/HealthChecks.Rabbitmq.v7.csproj
@@ -0,0 +1,24 @@
+
+
+
+ $(DefaultLibraryTargetFrameworks)
+ $(PackageTags);RabbitMQ
+ HealthChecks.RabbitMQ is the health check package for RabbitMQ.Client (version 7+).
+ $(HealthCheckRabbitMQ)
+ HealthChecks.Rabbitmq
+ HealthChecks.RabbitMQ
+ README.md
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/HealthChecks.Rabbitmq.v7/RabbitMQHealthCheck.cs b/src/HealthChecks.Rabbitmq.v7/RabbitMQHealthCheck.cs
new file mode 100644
index 0000000000..4de8e996f8
--- /dev/null
+++ b/src/HealthChecks.Rabbitmq.v7/RabbitMQHealthCheck.cs
@@ -0,0 +1,113 @@
+using System.Collections.Concurrent;
+using Microsoft.Extensions.Diagnostics.HealthChecks;
+using RabbitMQ.Client;
+
+namespace HealthChecks.RabbitMQ;
+
+///
+/// A health check for RabbitMQ services.
+///
+public class RabbitMQHealthCheck : IHealthCheck
+{
+ private static readonly ConcurrentDictionary> _connections = new();
+
+ private IConnection? _connection;
+ private readonly RabbitMQHealthCheckOptions _options;
+
+ public RabbitMQHealthCheck(RabbitMQHealthCheckOptions options)
+ {
+ _options = Guard.ThrowIfNull(options);
+ _connection = options.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 async Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
+ {
+ try
+ {
+ var connection = await EnsureConnectionAsync(cancellationToken).ConfigureAwait(false);
+ await using var model = await connection.CreateChannelAsync(cancellationToken: cancellationToken).ConfigureAwait(false);
+
+ return HealthCheckResult.Healthy();
+ }
+ catch (Exception ex)
+ {
+ 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/src/HealthChecks.Rabbitmq/HealthChecks.Rabbitmq.csproj b/src/HealthChecks.Rabbitmq/HealthChecks.Rabbitmq.csproj
index b81c68ab77..8f8195426d 100644
--- a/src/HealthChecks.Rabbitmq/HealthChecks.Rabbitmq.csproj
+++ b/src/HealthChecks.Rabbitmq/HealthChecks.Rabbitmq.csproj
@@ -9,7 +9,7 @@
-
+
diff --git a/test/HealthChecks.RabbitMQ.Tests/Functional/RabbitHealthCheckTests.cs b/test/HealthChecks.RabbitMQ.Tests/Functional/RabbitHealthCheckTests.cs
index 07c09d7713..d1791972d2 100644
--- a/test/HealthChecks.RabbitMQ.Tests/Functional/RabbitHealthCheckTests.cs
+++ b/test/HealthChecks.RabbitMQ.Tests/Functional/RabbitHealthCheckTests.cs
@@ -127,7 +127,11 @@ 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 =>
diff --git a/test/HealthChecks.RabbitMQ.Tests/HealthChecks.RabbitMQ.Tests.csproj b/test/HealthChecks.RabbitMQ.Tests/HealthChecks.RabbitMQ.Tests.csproj
index c2ed3dfd92..48b7d153dc 100644
--- a/test/HealthChecks.RabbitMQ.Tests/HealthChecks.RabbitMQ.Tests.csproj
+++ b/test/HealthChecks.RabbitMQ.Tests/HealthChecks.RabbitMQ.Tests.csproj
@@ -1,5 +1,9 @@
+
+ $(DefineConstants);RABBITMQ_V6
+
+
diff --git a/test/HealthChecks.RabbitMQ.v7.Tests/HealthChecks.RabbitMQ.v7.Tests.csproj b/test/HealthChecks.RabbitMQ.v7.Tests/HealthChecks.RabbitMQ.v7.Tests.csproj
new file mode 100644
index 0000000000..71aeca6d6c
--- /dev/null
+++ b/test/HealthChecks.RabbitMQ.v7.Tests/HealthChecks.RabbitMQ.v7.Tests.csproj
@@ -0,0 +1,16 @@
+
+
+
+ HealthChecks.RabbitMQ.Tests
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/HealthChecks.RabbitMQ.v7.Tests/HealthChecks.Rabbitmq.approved.txt b/test/HealthChecks.RabbitMQ.v7.Tests/HealthChecks.Rabbitmq.approved.txt
new file mode 100644
index 0000000000..e4e522964f
--- /dev/null
+++ b/test/HealthChecks.RabbitMQ.v7.Tests/HealthChecks.Rabbitmq.approved.txt
@@ -0,0 +1,28 @@
+namespace HealthChecks.RabbitMQ
+{
+ public class RabbitMQHealthCheck : Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck
+ {
+ public RabbitMQHealthCheck(HealthChecks.RabbitMQ.RabbitMQHealthCheckOptions options) { }
+ 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) { }
+ }
+}
\ No newline at end of file