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

wokrclient

by keisoft 2025. 2. 12.
// Worker.Shared 프로젝트 (공통 인터페이스 및 모델)
public interface IWorkerClient
{
    Task<WorkResponse> ProcessAsync(WorkRequest request);
    Task<WorkerHealthStatus> GetHealthStatusAsync();
    Task<List<string>> GetSupportedActionsAsync();
}

public class WorkerHealthStatus
{
    public bool IsHealthy { get; set; }
    public DateTime LastCheckTime { get; set; }
    public string Version { get; set; }
    public Dictionary<string, string> Metrics { get; set; }
}

// WorkerManager.Infrastructure 프로젝트
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);
    }

    public async Task<WorkResponse> ProcessAsync(WorkRequest request)
    {
        try
        {
            var response = await _httpClient.PostAsJsonAsync("api/worker/process", request);
            
            if (!response.IsSuccessStatusCode)
            {
                var errorContent = await response.Content.ReadAsStringAsync();
                _logger.LogError("Worker API returned error. Status: {StatusCode}, Error: {Error}", 
                    response.StatusCode, errorContent);
                
                throw new WorkerException($"Worker API error: {response.StatusCode}", errorContent);
            }

            return await response.Content.ReadFromJsonAsync<WorkResponse>() ?? 
                throw new WorkerException("Empty response from worker");
        }
        catch (Exception ex) when (ex is not WorkerException)
        {
            _logger.LogError(ex, "Error calling worker API");
            throw new WorkerException("Failed to process request", ex);
        }
    }

    public async Task<WorkerHealthStatus> GetHealthStatusAsync()
    {
        try
        {
            var response = await _httpClient.GetAsync("api/worker/health");
            response.EnsureSuccessStatusCode();
            
            return await response.Content.ReadFromJsonAsync<WorkerHealthStatus>() ?? 
                throw new WorkerException("Empty health status response");
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error checking worker health");
            return new WorkerHealthStatus 
            { 
                IsHealthy = false,
                LastCheckTime = DateTime.UtcNow,
                Metrics = new Dictionary<string, string>
                {
                    { "Error", ex.Message }
                }
            };
        }
    }

    public async Task<List<string>> GetSupportedActionsAsync()
    {
        try
        {
            var response = await _httpClient.GetAsync("api/worker/actions");
            response.EnsureSuccessStatusCode();
            
            return await response.Content.ReadFromJsonAsync<List<string>>() ?? 
                new List<string>();
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error getting supported actions");
            return new List<string>();
        }
    }
}

// Worker Client Options
public class WorkerClientOptions
{
    public string BaseUrl { get; set; } = "http://localhost:5000";
    public int TimeoutSeconds { get; set; } = 30;
    public int RetryCount { get; set; } = 3;
    public int RetryDelayMilliseconds { get; set; } = 1000;
}

// Custom Exception
public class WorkerException : Exception
{
    public string ErrorDetails { get; }

    public WorkerException(string message) : base(message)
    {
        ErrorDetails = string.Empty;
    }

    public WorkerException(string message, string errorDetails) : base(message)
    {
        ErrorDetails = errorDetails;
    }

    public WorkerException(string message, Exception innerException) : base(message, innerException)
    {
        ErrorDetails = innerException.Message;
    }
}

// Worker Client DI 등록을 위한 확장 메서드
public static class WorkerClientExtensions
{
    public static IServiceCollection AddWorkerClient(
        this IServiceCollection services,
        Action<WorkerClientOptions> configureOptions)
    {
        services.Configure(configureOptions);

        services.AddHttpClient<IWorkerClient, WorkerClient>()
            .AddPolicyHandler((provider, _) =>
            {
                var options = provider.GetRequiredService<IOptions<WorkerClientOptions>>().Value;
                
                return HttpPolicyExtensions
                    .HandleTransientHttpError()
                    .WaitAndRetryAsync(
                        options.RetryCount,
                        retryAttempt => TimeSpan.FromMilliseconds(
                            options.RetryDelayMilliseconds * Math.Pow(2, retryAttempt - 1)),
                        onRetry: (exception, timeSpan, retryCount, context) =>
                        {
                            var logger = provider.GetRequiredService<ILogger<WorkerClient>>();
                            logger.LogWarning(
                                exception, 
                                "Retry {RetryCount} after {RetryDelay}ms",
                                retryCount,
                                timeSpan.TotalMilliseconds);
                        });
            });

        return services;
    }
}

// Program.cs에서의 등록 예시
public static class Program
{
    public static void Main(string[] args)
    {
        var builder = WebApplication.CreateBuilder(args);

        // Worker Client 등록
        builder.Services.AddWorkerClient(options =>
        {
            options.BaseUrl = builder.Configuration["WorkerApi:BaseUrl"];
            options.TimeoutSeconds = builder.Configuration.GetValue<int>("WorkerApi:TimeoutSeconds");
            options.RetryCount = builder.Configuration.GetValue<int>("WorkerApi:RetryCount");
            options.RetryDelayMilliseconds = builder.Configuration.GetValue<int>("WorkerApi:RetryDelayMilliseconds");
        });

        // ... 나머지 서비스 등록
    }
}

// appsettings.json 설정 예시
{
  "WorkerApi": {
    "BaseUrl": "http://localhost:5000",
    "TimeoutSeconds": 30,
    "RetryCount": 3,
    "RetryDelayMilliseconds": 1000
  }
}