-
Notifications
You must be signed in to change notification settings - Fork 803
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Use recommended NATS.Net client for Nats package (#2336)
* Use recommnded NATS.Net client for nats package * Try resolve NatsConnection before INatsConnection * Update readme
- Loading branch information
Showing
9 changed files
with
171 additions
and
347 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,111 +1,26 @@ | ||
using System.Text; | ||
using Microsoft.Extensions.Diagnostics.HealthChecks; | ||
using NATS.Client; | ||
using NATS.Client.Core; | ||
|
||
namespace HealthChecks.Nats; | ||
|
||
/// <summary> | ||
/// Health check for Nats Server. | ||
/// </summary> | ||
/// <remarks> | ||
/// Relies on a static <see cref="ConnectionFactory"/> which provides factory methods to create | ||
/// connections to NATS Servers, and a <see cref="IConnection"/> object connected to the NATS server. | ||
/// </remarks> | ||
public sealed class NatsHealthCheck : IHealthCheck, IDisposable | ||
public sealed class NatsHealthCheck(INatsConnection connection) : IHealthCheck | ||
{ | ||
private static readonly ConnectionFactory _connectionFactory = new(); | ||
public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) => | ||
await TryConnectAsync(connection).ConfigureAwait(false); | ||
|
||
private readonly NatsOptions _options; | ||
|
||
private IConnection? _connection; | ||
|
||
public NatsHealthCheck(NatsOptions natsOptions) | ||
{ | ||
_options = Guard.ThrowIfNull(natsOptions); | ||
} | ||
|
||
/// <inheritdoc /> | ||
public Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) | ||
private static async Task<HealthCheckResult> TryConnectAsync(INatsConnection natsConnection) | ||
{ | ||
try | ||
{ | ||
// Create new connection if there is no existing one | ||
IConnection? connection = _connection; | ||
if (connection == null) | ||
{ | ||
#pragma warning disable IDISP001 // Dispose created [false positive, https://github.com/DotNetAnalyzers/IDisposableAnalyzers/issues/515] | ||
connection = CreateConnection(_options); | ||
#pragma warning restore IDISP001 // Dispose created | ||
var exchanged = Interlocked.CompareExchange(ref _connection, connection, null); | ||
if (exchanged != null) // was set by other thread | ||
{ | ||
connection.Dispose(); | ||
connection = exchanged; | ||
} | ||
} | ||
|
||
// reset connection in case of stuck so the next HC call will establish it again | ||
// https://github.com/Xabaril/AspNetCore.Diagnostics.HealthChecks/issues/1544 | ||
if (connection.State == ConnState.DISCONNECTED || connection.State == ConnState.CLOSED) | ||
_connection = null; | ||
|
||
var healthCheckResult = GetHealthCheckResultFromState(connection); | ||
return Task.FromResult(healthCheckResult); | ||
await natsConnection.ConnectAsync().ConfigureAwait(false); | ||
return HealthCheckResult.Healthy(); | ||
} | ||
catch (Exception ex) | ||
{ | ||
var unhealthy = new HealthCheckResult(context.Registration.FailureStatus, exception: ex); | ||
return Task.FromResult(unhealthy); | ||
} | ||
|
||
IConnection CreateConnection(NatsOptions options) | ||
catch (Exception) | ||
{ | ||
if (!string.IsNullOrWhiteSpace(options.CredentialsPath)) | ||
return _connectionFactory.CreateConnection(options.Url, options.CredentialsPath); | ||
if (!string.IsNullOrWhiteSpace(options.Jwt) && !string.IsNullOrWhiteSpace(options.PrivateNKey)) | ||
return _connectionFactory.CreateConnection(options.Url, options.Jwt, options.PrivateNKey); | ||
return _connectionFactory.CreateConnection(options.Url); | ||
return HealthCheckResult.Unhealthy(); | ||
} | ||
|
||
HealthCheckResult GetHealthCheckResultFromState(IConnection connection) | ||
{ | ||
string description = GetDescription(connection); | ||
|
||
return connection.State switch | ||
{ | ||
ConnState.CONNECTED => HealthCheckResult.Healthy(description, GetStatsData(connection)), | ||
ConnState.CONNECTING | ||
or ConnState.RECONNECTING | ||
or ConnState.DRAINING_SUBS | ||
or ConnState.DRAINING_PUBS => HealthCheckResult.Degraded(description), | ||
ConnState.CLOSED | ||
or ConnState.DISCONNECTED => HealthCheckResult.Unhealthy(description), | ||
_ => new HealthCheckResult(context.Registration.FailureStatus, description), | ||
}; | ||
} | ||
|
||
static string GetDescription(IConnection connection) | ||
{ | ||
var sb = new StringBuilder(); | ||
sb.AppendFormat("{0}: {1}; ", nameof(connection.ClientIP), connection.ClientIP); | ||
if (!string.IsNullOrWhiteSpace(connection.ConnectedUrl)) | ||
sb.AppendFormat("{0}: {1}; ", nameof(connection.ConnectedUrl), connection.ConnectedUrl); | ||
sb.AppendFormat("{0}: {1}; ", nameof(connection.State), connection.State); | ||
if (connection.SubscriptionCount != default) | ||
sb.AppendFormat("{0}: {1}", nameof(connection.SubscriptionCount), connection.SubscriptionCount); | ||
return sb.ToString(); | ||
} | ||
|
||
static IReadOnlyDictionary<string, object> GetStatsData(IConnection connection) => | ||
new Dictionary<string, object> | ||
{ | ||
[nameof(connection.Stats.InMsgs)] = connection.Stats.InMsgs, | ||
[nameof(connection.Stats.OutMsgs)] = connection.Stats.OutMsgs, | ||
[nameof(connection.Stats.InBytes)] = connection.Stats.InBytes, | ||
[nameof(connection.Stats.OutBytes)] = connection.Stats.OutBytes, | ||
[nameof(connection.Stats.Reconnects)] = connection.Stats.Reconnects | ||
}; | ||
} | ||
|
||
public void Dispose() => _connection?.Dispose(); | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.