fix: RabbitMQ 연결 실패 시 앱 크래시 방지 (#124) #125
|
|
@ -4,6 +4,7 @@ using Microsoft.EntityFrameworkCore;
|
||||||
using Swashbuckle.AspNetCore.Annotations;
|
using Swashbuckle.AspNetCore.Annotations;
|
||||||
using SPMS.Domain.Common;
|
using SPMS.Domain.Common;
|
||||||
using SPMS.Infrastructure;
|
using SPMS.Infrastructure;
|
||||||
|
using SPMS.Infrastructure.Messaging;
|
||||||
|
|
||||||
namespace SPMS.API.Controllers;
|
namespace SPMS.API.Controllers;
|
||||||
|
|
||||||
|
|
@ -14,10 +15,17 @@ namespace SPMS.API.Controllers;
|
||||||
public class PublicController : ControllerBase
|
public class PublicController : ControllerBase
|
||||||
{
|
{
|
||||||
private readonly AppDbContext _dbContext;
|
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;
|
_dbContext = dbContext;
|
||||||
|
_rabbitConnection = rabbitConnection;
|
||||||
|
_rabbitInitializer = rabbitInitializer;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("health")]
|
[HttpPost("health")]
|
||||||
|
|
@ -39,11 +47,26 @@ public class PublicController : ControllerBase
|
||||||
allHealthy = false;
|
allHealthy = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Redis 연결 확인 (Phase 2에서 구현 예정)
|
// 2. Redis 연결 확인 (Phase 3-2에서 구현 예정)
|
||||||
checks["redis"] = new { status = "not_configured" };
|
checks["redis"] = new { status = "not_configured" };
|
||||||
|
|
||||||
// 3. RabbitMQ 연결 확인 (Phase 2에서 구현 예정)
|
// 3. RabbitMQ 연결 확인
|
||||||
checks["rabbitmq"] = new { status = "not_configured" };
|
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)
|
if (allHealthy)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,8 @@ public static class DependencyInjection
|
||||||
// RabbitMQ
|
// RabbitMQ
|
||||||
services.Configure<RabbitMQSettings>(configuration.GetSection(RabbitMQSettings.SectionName));
|
services.Configure<RabbitMQSettings>(configuration.GetSection(RabbitMQSettings.SectionName));
|
||||||
services.AddSingleton<RabbitMQConnection>();
|
services.AddSingleton<RabbitMQConnection>();
|
||||||
services.AddHostedService<RabbitMQInitializer>();
|
services.AddSingleton<RabbitMQInitializer>();
|
||||||
|
services.AddHostedService(sp => sp.GetRequiredService<RabbitMQInitializer>());
|
||||||
services.AddScoped<IPushQueueService, PushQueueService>();
|
services.AddScoped<IPushQueueService, PushQueueService>();
|
||||||
|
|
||||||
// Push Senders
|
// Push Senders
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,8 @@ public class RabbitMQConnection : IAsyncDisposable
|
||||||
private readonly SemaphoreSlim _semaphore = new(1, 1);
|
private readonly SemaphoreSlim _semaphore = new(1, 1);
|
||||||
private IConnection? _connection;
|
private IConnection? _connection;
|
||||||
|
|
||||||
|
public bool IsConnected => _connection is { IsOpen: true };
|
||||||
|
|
||||||
public RabbitMQConnection(
|
public RabbitMQConnection(
|
||||||
IOptions<RabbitMQSettings> settings,
|
IOptions<RabbitMQSettings> settings,
|
||||||
ILogger<RabbitMQConnection> logger)
|
ILogger<RabbitMQConnection> logger)
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,14 @@ using SPMS.Application.Settings;
|
||||||
|
|
||||||
namespace SPMS.Infrastructure.Messaging;
|
namespace SPMS.Infrastructure.Messaging;
|
||||||
|
|
||||||
public class RabbitMQInitializer : IHostedService
|
public class RabbitMQInitializer : BackgroundService
|
||||||
{
|
{
|
||||||
private readonly RabbitMQConnection _connection;
|
private readonly RabbitMQConnection _connection;
|
||||||
private readonly RabbitMQSettings _settings;
|
private readonly RabbitMQSettings _settings;
|
||||||
private readonly ILogger<RabbitMQInitializer> _logger;
|
private readonly ILogger<RabbitMQInitializer> _logger;
|
||||||
|
private static readonly TimeSpan RetryInterval = TimeSpan.FromSeconds(30);
|
||||||
|
|
||||||
|
public bool IsInitialized { get; private set; }
|
||||||
|
|
||||||
public RabbitMQInitializer(
|
public RabbitMQInitializer(
|
||||||
RabbitMQConnection connection,
|
RabbitMQConnection connection,
|
||||||
|
|
@ -22,19 +25,26 @@ public class RabbitMQInitializer : IHostedService
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task StartAsync(CancellationToken cancellationToken)
|
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||||
|
{
|
||||||
|
while (!stoppingToken.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await InitializeAsync(cancellationToken);
|
await InitializeAsync(stoppingToken);
|
||||||
|
IsInitialized = true;
|
||||||
|
_logger.LogInformation("RabbitMQ 초기화 완료");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.LogWarning(ex, "RabbitMQ 초기화 실패 — 서버는 계속 실행됩니다. 메시지 발송 시 재연결을 시도합니다.");
|
_logger.LogWarning(ex, "RabbitMQ 초기화 실패 — {RetrySeconds}초 후 재시도합니다.", RetryInterval.TotalSeconds);
|
||||||
|
await Task.Delay(RetryInterval, stoppingToken);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task InitializeAsync(CancellationToken cancellationToken)
|
private async Task InitializeAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
await using var channel = await _connection.CreateChannelAsync(cancellationToken);
|
await using var channel = await _connection.CreateChannelAsync(cancellationToken);
|
||||||
|
|
||||||
|
|
@ -87,6 +97,4 @@ public class RabbitMQInitializer : IHostedService
|
||||||
_logger.LogInformation("Queue 선언 및 바인딩 완료: {Queue} → {Exchange} (routing_key: schedule)",
|
_logger.LogInformation("Queue 선언 및 바인딩 완료: {Queue} → {Exchange} (routing_key: schedule)",
|
||||||
_settings.ScheduleQueue, _settings.Exchange);
|
_settings.ScheduleQueue, _settings.Exchange);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user