SPMS_API/SPMS.Infrastructure/Messaging/RabbitMQConnection.cs
seonkyu.kim 3fc3bb8144 fix: RabbitMQ 상태 모니터링 및 백그라운드 재시도 추가 (#124)
- RabbitMQInitializer를 BackgroundService로 변경 (30초 간격 재시도)
- RabbitMQConnection에 IsConnected 속성 추가
- Health check에 RabbitMQ 연결/초기화 상태 반영
- DI 등록 변경 (Singleton + HostedService 패턴)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 19:15:42 +09:00

82 lines
2.5 KiB
C#

using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using RabbitMQ.Client;
using SPMS.Application.Settings;
namespace SPMS.Infrastructure.Messaging;
public class RabbitMQConnection : IAsyncDisposable
{
private readonly RabbitMQSettings _settings;
private readonly ILogger<RabbitMQConnection> _logger;
private readonly SemaphoreSlim _semaphore = new(1, 1);
private IConnection? _connection;
public bool IsConnected => _connection is { IsOpen: true };
public RabbitMQConnection(
IOptions<RabbitMQSettings> settings,
ILogger<RabbitMQConnection> logger)
{
_settings = settings.Value;
_logger = logger;
}
public async Task<IConnection> GetConnectionAsync(CancellationToken cancellationToken = default)
{
if (_connection is { IsOpen: true })
return _connection;
await _semaphore.WaitAsync(cancellationToken);
try
{
if (_connection is { IsOpen: true })
return _connection;
var factory = new ConnectionFactory
{
HostName = _settings.HostName,
Port = _settings.Port,
UserName = _settings.UserName,
Password = _settings.Password,
VirtualHost = _settings.VirtualHost,
AutomaticRecoveryEnabled = true,
NetworkRecoveryInterval = TimeSpan.FromSeconds(10),
RequestedHeartbeat = TimeSpan.FromSeconds(60)
};
_connection = await factory.CreateConnectionAsync(cancellationToken);
_logger.LogInformation("RabbitMQ 연결 성공: {HostName}:{Port}", _settings.HostName, _settings.Port);
return _connection;
}
catch (Exception ex)
{
_logger.LogError(ex, "RabbitMQ 연결 실패: {HostName}:{Port}", _settings.HostName, _settings.Port);
throw;
}
finally
{
_semaphore.Release();
}
}
public async Task<IChannel> CreateChannelAsync(CancellationToken cancellationToken = default)
{
var connection = await GetConnectionAsync(cancellationToken);
return await connection.CreateChannelAsync(cancellationToken: cancellationToken);
}
public async ValueTask DisposeAsync()
{
if (_connection is not null)
{
await _connection.CloseAsync();
_connection.Dispose();
_connection = null;
}
_semaphore.Dispose();
}
}