// 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
}
}
카테고리 없음