MailQueueWorker.cs 3.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. using Microsoft.EntityFrameworkCore;
  2. using bitforum.Constants;
  3. using bitforum.Services;
  4. namespace bitforum.Workers
  5. {
  6. public class MailQueueWorker : BackgroundService
  7. {
  8. private readonly ILogger<MailQueueWorker> _logger;
  9. private readonly IServiceScopeFactory _scopeFactory;
  10. private readonly int _workerCount = 2; // 병렬 처리할 Worker 개수
  11. public MailQueueWorker(ILogger<MailQueueWorker> logger, IServiceScopeFactory scopeFactory)
  12. {
  13. _logger = logger;
  14. _scopeFactory = scopeFactory;
  15. }
  16. protected override async Task ExecuteAsync(CancellationToken stoppingToken)
  17. {
  18. _logger.LogInformation("MailQueueWorker 시작됨.");
  19. // 병렬 작업자 처리 지시
  20. await Task.WhenAll(
  21. Enumerable.Range(0, _workerCount).Select(_ => Task.Run(() => ProcessQueueAsync(stoppingToken))).ToArray()
  22. );
  23. }
  24. private async Task ProcessQueueAsync(CancellationToken stoppingToken)
  25. {
  26. var mailService = _scopeFactory.CreateScope().ServiceProvider.GetRequiredService<IMailService>();
  27. while (!stoppingToken.IsCancellationRequested)
  28. {
  29. using (var scope = _scopeFactory.CreateScope())
  30. {
  31. var db = scope.ServiceProvider.GetRequiredService<DefaultDbContext>();
  32. // 발송되지 않는 메일 확인
  33. var email = await db.EmailLog.Where(x => x.Status == MailStatus.Pending || x.Status == MailStatus.Processing).OrderBy(x => x.CreatedAt).Take(1).FirstOrDefaultAsync(stoppingToken);
  34. if (email is null)
  35. {
  36. await Task.Delay(3000, stoppingToken);
  37. continue;
  38. }
  39. try
  40. {
  41. // 상태를 "Processing"으로 변경하여 중복 처리 방지
  42. email.Status = MailStatus.Processing;
  43. await db.SaveChangesAsync(stoppingToken);
  44. // 메일 전송 시도
  45. await mailService.SendEmailAsync(new SendData(
  46. email.ToAddress, email.Subject, email.Message ?? string.Empty
  47. ));
  48. // 성공 시 "Sent"로 변경
  49. email.Status = MailStatus.Sent;
  50. }
  51. catch (Exception e)
  52. {
  53. _logger.LogError(e, "메일 전송 중 오류 발생");
  54. email.Status = MailStatus.Failed;
  55. continue;
  56. }
  57. email.ProcessedAt = DateTime.UtcNow;
  58. await db.SaveChangesAsync(stoppingToken);
  59. }
  60. }
  61. }
  62. public override async Task StopAsync(CancellationToken stoppingToken)
  63. {
  64. _logger.LogInformation($"{nameof(MailQueueWorker)} is stopping.");
  65. await base.StopAsync(stoppingToken);
  66. }
  67. }
  68. }