// 1. Shared Models (Shared.Models 프로젝트)
/// <summary>
/// 작업 요청을 나타내는 클래스
/// </summary>
public class WorkRequest
{
/// <summary>작업 유형 - "Sync" 또는 "Async"</summary>
public string ActionType { get; set; }
/// <summary>처리할 데이터</summary>
public string Data { get; set; }
/// <summary>작업 처리에 필요한 추가 매개변수</summary>
public Dictionary<string, string> Parameters { get; set; }
/// <summary>요청 시간 (UTC)</summary>
public DateTime RequestTime { get; set; } = DateTime.UtcNow;
}
/// <summary>
/// 작업 응답을 나타내는 클래스
/// </summary>
public class WorkResponse
{
/// <summary>작업 성공 여부</summary>
public bool Success { get; set; }
/// <summary>작업 결과</summary>
public string Result { get; set; }
/// <summary>오류 메시지 (실패 시)</summary>
public string ErrorMessage { get; set; }
/// <summary>응답 시간</summary>
public DateTime ResponseTime { get; set; }
}
/// <summary>
/// 작업 우선순위를 정의하는 열거형
/// </summary>
public enum WorkPriority
{
Low = 0, // 낮은 우선순위
Normal = 1, // 일반 우선순위
High = 2 // 높은 우선순위
}
// 2. Blazor SSR Client (BlazorApp.Client 프로젝트)
/// <summary>
/// Worker Manager와의 통신을 담당하는 서비스 인터페이스
/// </summary>
public interface IWorkerManagerService
{
Task<WorkResponse> ProcessRequestAsync(WorkRequest request, WorkPriority priority = WorkPriority.Normal);
Task<WorkerManagerStatus> GetStatusAsync();
}
/// <summary>
/// Worker Manager 서비스 구현 클래스
/// </summary>
public class WorkerManagerService : IWorkerManagerService
{
private readonly HttpClient _httpClient;
private readonly ILogger<WorkerManagerService> _logger;
/// <summary>
/// HTTP 클라이언트와 로거를 주입받는 생성자
/// </summary>
public WorkerManagerService(HttpClient httpClient, ILogger<WorkerManagerService> logger)
{
_httpClient = httpClient;
_logger = logger;
}
/// <summary>
/// 작업 요청을 처리하는 메서드
/// </summary>
/// <param name="request">작업 요청 정보</param>
/// <param name="priority">작업 우선순위</param>
public async Task<WorkResponse> ProcessRequestAsync(WorkRequest request, WorkPriority priority = WorkPriority.Normal)
{
try
{
var response = await _httpClient.PostAsJsonAsync("api/workermanager/process", new { Request = request, Priority = priority });
response.EnsureSuccessStatusCode();
return await response.Content.ReadFromJsonAsync<WorkResponse>();
}
catch (Exception ex)
{
_logger.LogError(ex, "Error processing request");
throw;
}
}
/// <summary>
/// Worker Manager의 현재 상태를 조회하는 메서드
/// </summary>
public async Task<WorkerManagerStatus> GetStatusAsync()
{
try
{
var response = await _httpClient.GetAsync("api/workermanager/status");
response.EnsureSuccessStatusCode();
return await response.Content.ReadFromJsonAsync<WorkerManagerStatus>();
}
catch (Exception ex)
{
_logger.LogError(ex, "Error getting status");
throw;
}
}
}
// Blazor 모니터링 페이지 컴포넌트
/// <summary>
/// Worker 작업 현황을 모니터링하는 Blazor 페이지
/// </summary>
@page "/monitor"
@inject IWorkerManagerService WorkerManagerService
@implements IDisposable
// ... (UI 마크업 부분은 자체 설명적이므로 주석 생략)
@code {
private WorkerManagerStatus status;
private Timer timer;
/// <summary>
/// 컴포넌트 초기화 시 1초 간격으로 상태 업데이트 타이머 설정
/// </summary>
protected override void OnInitialized()
{
timer = new Timer(async _ =>
{
status = await WorkerManagerService.GetStatusAsync();
await InvokeAsync(StateHasChanged);
}, null, 0, 1000);
}
/// <summary>
/// 컴포넌트 제거 시 타이머 정리
/// </summary>
public void Dispose()
{
timer?.Dispose();
}
}
// 3. Worker Manager (WorkerManager.API 프로젝트)
/// <summary>
/// Worker Manager API 컨트롤러
/// </summary>
[ApiController]
[Route("api/[controller]")]
public class WorkerManagerController : ControllerBase
{
private readonly IWorkerManagerService _workerManagerService;
public WorkerManagerController(IWorkerManagerService workerManagerService)
{
_workerManagerService = workerManagerService;
}
/// <summary>
/// 작업 요청을 처리하는 엔드포인트
/// </summary>
[HttpPost("process")]
public async Task<IActionResult> ProcessRequest([FromBody] WorkRequest request)
{
var result = await _workerManagerService.ProcessRequestAsync(request);
return Ok(result);
}
/// <summary>
/// Worker Manager 상태를 조회하는 엔드포인트
/// </summary>
[HttpGet("status")]
public async Task<IActionResult> GetStatus()
{
var status = await _workerManagerService.GetStatusAsync();
return Ok(status);
}
}
/// <summary>
/// Worker Manager 서비스 구현
/// 작업 큐 관리 및 작업자 스레드 조정을 담당
/// </summary>
public class WorkerManagerService : IWorkerManagerService
{
private readonly IWorkerClient _workerClient;
private readonly WorkQueue _workQueue;
private readonly Dictionary<string, TaskCompletionSource<WorkResponse>> _pendingTasks;
private readonly SemaphoreSlim _workerSemaphore;
private readonly ConcurrentDictionary<string, WorkItemStatus> _activeWorks;
private const int MaxConcurrentWorkers = 3; // 최대 동시 작업자 수
/// <summary>
/// 비동기 작업 처리
/// </summary>
/// <param name="request">작업 요청</param>
/// <param name="priority">우선순위</param>
public async Task<WorkResponse> ProcessRequestAsync(WorkRequest request, WorkPriority priority = WorkPriority.Normal)
{
var taskCompletionSource = new TaskCompletionSource<WorkResponse>();
var requestId = Guid.NewGuid().ToString();
// 작업 큐에 등록할 항목 생성
var queuedWork = new QueuedWork
{
RequestId = requestId,
Request = request,
Priority = priority,
EnqueueTime = DateTime.UtcNow,
RetryCount = 0
};
// 비동기 작업은 즉시 응답
if (request.ActionType == "Async")
{
_workQueue.Enqueue(queuedWork);
_ = ProcessQueueAsync();
return new WorkResponse
{
Success = true,
Result = $"작업이 큐에 등록되었습니다. RequestId: {requestId}",
ResponseTime = DateTime.UtcNow
};
}
// 동기 작업은 완료될 때까지 대기
lock (_pendingTasks)
{
_pendingTasks[requestId] = taskCompletionSource;
}
_workQueue.Enqueue(queuedWork);
_ = ProcessQueueAsync();
// 5분 타임아웃 설정
using var cts = new CancellationTokenSource(TimeSpan.FromMinutes(5));
try
{
var response = await taskCompletionSource.Task.WaitAsync(cts.Token);
response.ResponseTime = DateTime.UtcNow;
return response;
}
catch (OperationCanceledException)
{
throw new TimeoutException("Worker processing timed out");
}
}
// ... (나머지 메서드들도 비슷한 방식으로 주석 처리)
}
// 4. Worker Client (WorkerManager.Infrastructure 프로젝트)
/// <summary>
/// Worker API와의 통신을 담당하는 클라이언트 인터페이스
/// </summary>
public interface IWorkerClient
{
Task<WorkResponse> ProcessAsync(WorkRequest request);
Task<WorkerHealthStatus> GetHealthStatusAsync();
}
/// <summary>
/// Worker 클라이언트 구현
/// Worker API와의 HTTP 통신을 처리
/// </summary>
public class WorkerClient : IWorkerClient
{
private readonly HttpClient _httpClient;
private readonly ILogger<WorkerClient> _logger;
private readonly IOptions<WorkerClientOptions> _options;
public WorkerClient(
HttpClient httpClient,
ILogger<WorkerClient> logger,
IOptions<WorkerClientOptions> options)
{
_httpClient = httpClient;
_logger = logger;
_options = options;
_httpClient.BaseAddress = new Uri(options.Value.BaseUrl);
_httpClient.Timeout = TimeSpan.FromSeconds(options.Value.TimeoutSeconds);
}
/// <summary>
/// Worker API에 작업 처리 요청
/// </summary>
public async Task<WorkResponse> ProcessAsync(WorkRequest request)
{
try
{
var response = await _httpClient.PostAsJsonAsync("api/worker/process", request);
response.EnsureSuccessStatusCode();
return await response.Content.ReadFromJsonAsync<WorkResponse>();
}
catch (Exception ex)
{
_logger.LogError(ex, "Error processing request");
throw;
}
}
/// <summary>
/// Worker의 상태 확인
/// </summary>
public async Task<WorkerHealthStatus> GetHealthStatusAsync()
{
try
{
var response = await _httpClient.GetAsync("api/worker/health");
response.EnsureSuccessStatusCode();
return await response.Content.ReadFromJsonAsync<WorkerHealthStatus>();
}
catch (Exception ex)
{
_logger.LogError(ex, "Error checking worker health");
return new WorkerHealthStatus { IsHealthy = false };
}
}
}
// 5. Worker API (Worker.API 프로젝트)
/// <summary>
/// Worker API 컨트롤러
/// 실제 작업 처리 및 상태 모니터링을 담당
/// </summary>
[ApiController]
[Route("api/[controller]")]
public class WorkerController : ControllerBase
{
private readonly ICustomActionService _customActionService;
private readonly IBackgroundTaskQueue _taskQueue;
private readonly ILogger<WorkerController> _logger;
public WorkerController(
ICustomActionService customActionService,
IBackgroundTaskQueue taskQueue,
ILogger<WorkerController> logger)
{
_customActionService = customActionService;
_taskQueue = taskQueue;
_logger = logger;
}
/// <summary>
/// 작업 처리 엔드포인트
/// 동기/비동기 작업을 구분하여 처리
/// </summary>
[HttpPost("process")]
public async Task<IActionResult> Process([FromBody] WorkRequest request)
{
try
{
// 비동기 작업은 백그라운드 큐에 등록
if (request.ActionType == "Async")
{
await _taskQueue.QueueBackgroundWorkItemAsync(async token =>
{
await _customActionService.ExecuteActionAsync(request.Data);
});
return Ok(new WorkResponse
{
Success = true,
Result = "작업이 백그라운드에서 실행됩니다.",
ResponseTime = DateTime.UtcNow
});
}
// 동기 작업은 즉시 실행
var result = await _customActionService.ExecuteActionAsync(request.Data);
return Ok(new WorkResponse
{
Success = true,
Result = result,
ResponseTime = DateTime.UtcNow
});
}
catch (Exception ex)
{
_logger.LogError(ex, "Error processing request");
return StatusCode(500, new WorkResponse
{
Success = false,
ErrorMessage = ex.Message,
ResponseTime = DateTime.UtcNow
});
}
}
/// <summary>
/// Worker 상태 확인 엔드포인트
/// </summary>
[HttpGet("health")]
public IActionResult GetHealth()
{
return Ok(new WorkerHealthStatus
{
IsHealthy = true,
LastCheckTime = DateTime.UtcNow
});
}
}
카테고리 없음