본문 바로가기
카테고리 없음

주석1

by keisoft 2025. 2. 12.

// 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
        });
    }
}