fix: RabbitMQ 연결 실패 시 앱 크래시 방지 (#124) #125
|
|
@ -4,6 +4,7 @@ using Microsoft.EntityFrameworkCore;
|
|||
using Swashbuckle.AspNetCore.Annotations;
|
||||
using SPMS.Domain.Common;
|
||||
using SPMS.Infrastructure;
|
||||
using SPMS.Infrastructure.Messaging;
|
||||
|
||||
namespace SPMS.API.Controllers;
|
||||
|
||||
|
|
@ -14,10 +15,17 @@ namespace SPMS.API.Controllers;
|
|||
public class PublicController : ControllerBase
|
||||
{
|
||||
private readonly AppDbContext _dbContext;
|
||||
private readonly RabbitMQConnection _rabbitConnection;
|
||||
private readonly RabbitMQInitializer _rabbitInitializer;
|
||||
|
||||
public PublicController(AppDbContext dbContext)
|
||||
public PublicController(
|
||||
AppDbContext dbContext,
|
||||
RabbitMQConnection rabbitConnection,
|
||||
RabbitMQInitializer rabbitInitializer)
|
||||
{
|
||||
_dbContext = dbContext;
|
||||
_rabbitConnection = rabbitConnection;
|
||||
_rabbitInitializer = rabbitInitializer;
|
||||
}
|
||||
|
||||
[HttpPost("health")]
|
||||
|
|
@ -39,11 +47,26 @@ public class PublicController : ControllerBase
|
|||
allHealthy = false;
|
||||
}
|
||||
|
||||
// 2. Redis 연결 확인 (Phase 2에서 구현 예정)
|
||||
// 2. Redis 연결 확인 (Phase 3-2에서 구현 예정)
|
||||
checks["redis"] = new { status = "not_configured" };
|
||||
|
||||
// 3. RabbitMQ 연결 확인 (Phase 2에서 구현 예정)
|
||||
checks["rabbitmq"] = new { status = "not_configured" };
|
||||
// 3. RabbitMQ 연결 확인
|
||||
var rabbitConnected = _rabbitConnection.IsConnected;
|
||||
var rabbitInitialized = _rabbitInitializer.IsInitialized;
|
||||
if (rabbitConnected && rabbitInitialized)
|
||||
{
|
||||
checks["rabbitmq"] = new { status = "healthy" };
|
||||
}
|
||||
else
|
||||
{
|
||||
checks["rabbitmq"] = new
|
||||
{
|
||||
status = "unhealthy",
|
||||
connected = rabbitConnected,
|
||||
initialized = rabbitInitialized
|
||||
};
|
||||
allHealthy = false;
|
||||
}
|
||||
|
||||
if (allHealthy)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -58,7 +58,8 @@ public static class DependencyInjection
|
|||
// RabbitMQ
|
||||
services.Configure<RabbitMQSettings>(configuration.GetSection(RabbitMQSettings.SectionName));
|
||||
services.AddSingleton<RabbitMQConnection>();
|
||||
services.AddHostedService<RabbitMQInitializer>();
|
||||
services.AddSingleton<RabbitMQInitializer>();
|
||||
services.AddHostedService(sp => sp.GetRequiredService<RabbitMQInitializer>());
|
||||
services.AddScoped<IPushQueueService, PushQueueService>();
|
||||
|
||||
// Push Senders
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ public class RabbitMQConnection : IAsyncDisposable
|
|||
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)
|
||||
|
|
|
|||
|
|
@ -6,11 +6,14 @@ using SPMS.Application.Settings;
|
|||
|
||||
namespace SPMS.Infrastructure.Messaging;
|
||||
|
||||
public class RabbitMQInitializer : IHostedService
|
||||
public class RabbitMQInitializer : BackgroundService
|
||||
{
|
||||
private readonly RabbitMQConnection _connection;
|
||||
private readonly RabbitMQSettings _settings;
|
||||
private readonly ILogger<RabbitMQInitializer> _logger;
|
||||
private static readonly TimeSpan RetryInterval = TimeSpan.FromSeconds(30);
|
||||
|
||||
public bool IsInitialized { get; private set; }
|
||||
|
||||
public RabbitMQInitializer(
|
||||
RabbitMQConnection connection,
|
||||
|
|
@ -22,13 +25,29 @@ public class RabbitMQInitializer : IHostedService
|
|||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task StartAsync(CancellationToken cancellationToken)
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
while (!stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
await InitializeAsync(stoppingToken);
|
||||
IsInitialized = true;
|
||||
_logger.LogInformation("RabbitMQ 초기화 완료");
|
||||
return;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "RabbitMQ 초기화 실패 — {RetrySeconds}초 후 재시도합니다.", RetryInterval.TotalSeconds);
|
||||
await Task.Delay(RetryInterval, stoppingToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task InitializeAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
await using var channel = await _connection.CreateChannelAsync(cancellationToken);
|
||||
|
||||
// Exchange 선언: Direct, Durable
|
||||
await channel.ExchangeDeclareAsync(
|
||||
exchange: _settings.Exchange,
|
||||
type: ExchangeType.Direct,
|
||||
|
|
@ -44,7 +63,6 @@ public class RabbitMQInitializer : IHostedService
|
|||
{ "x-message-ttl", _settings.MessageTtl }
|
||||
};
|
||||
|
||||
// Push Queue 선언
|
||||
await channel.QueueDeclareAsync(
|
||||
queue: _settings.PushQueue,
|
||||
durable: true,
|
||||
|
|
@ -62,7 +80,6 @@ public class RabbitMQInitializer : IHostedService
|
|||
_logger.LogInformation("Queue 선언 및 바인딩 완료: {Queue} → {Exchange} (routing_key: push)",
|
||||
_settings.PushQueue, _settings.Exchange);
|
||||
|
||||
// Schedule Queue 선언
|
||||
await channel.QueueDeclareAsync(
|
||||
queue: _settings.ScheduleQueue,
|
||||
durable: true,
|
||||
|
|
@ -80,12 +97,4 @@ public class RabbitMQInitializer : IHostedService
|
|||
_logger.LogInformation("Queue 선언 및 바인딩 완료: {Queue} → {Exchange} (routing_key: schedule)",
|
||||
_settings.ScheduleQueue, _settings.Exchange);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "RabbitMQ Exchange/Queue 초기화 실패");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user