feat(Worker): Implementa política de reintentos con Polly para resiliencia en la obtención de datos
This commit is contained in:
		| @@ -69,7 +69,8 @@ namespace Mercados.Infrastructure.DataFetchers | ||||
|             _logger.LogInformation("Iniciando fetch para {SourceName}.", SourceName); | ||||
|             try | ||||
|             { | ||||
|                 var client = _httpClientFactory.CreateClient(); // Creamos el cliente una vez | ||||
|                 // Pedimos el cliente con el nombre correcto | ||||
|                 var client = _httpClientFactory.CreateClient("BcrDataFetcher"); | ||||
|  | ||||
|                 var token = await GetAuthTokenAsync(client); | ||||
|                 if (string.IsNullOrEmpty(token)) | ||||
|   | ||||
| @@ -36,7 +36,8 @@ namespace Mercados.Infrastructure.DataFetchers | ||||
|             { | ||||
|                 throw new InvalidOperationException("La clave de API de Finnhub no está configurada en appsettings.json (ApiKeys:Finnhub)"); | ||||
|             } | ||||
|             _client = new FinnhubClient(httpClientFactory.CreateClient("Finnhub"), apiKey); | ||||
|             // Le pasamos el cliente HTTP que ya está configurado con Polly en Program.cs | ||||
|             _client = new FinnhubClient(httpClientFactory.CreateClient("FinnhubDataFetcher"), apiKey); | ||||
|             _cotizacionRepository = cotizacionRepository; | ||||
|             _fuenteDatoRepository = fuenteDatoRepository; | ||||
|             _logger = logger; | ||||
|   | ||||
| @@ -61,7 +61,8 @@ namespace Mercados.Infrastructure.DataFetchers | ||||
|  | ||||
|         private async Task<string> GetHtmlContentAsync() | ||||
|         { | ||||
|             var client = _httpClientFactory.CreateClient(); | ||||
|             // Pedimos el cliente HTTP con el nombre específico que tiene la política de Polly | ||||
|             var client = _httpClientFactory.CreateClient("MercadoAgroFetcher"); | ||||
|             // Es importante simular un navegador para evitar bloqueos. | ||||
|             client.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"); | ||||
|  | ||||
|   | ||||
| @@ -9,6 +9,7 @@ | ||||
|     <PackageReference Include="Dapper" Version="2.1.66" /> | ||||
|     <PackageReference Include="Microsoft.Data.SqlClient" Version="6.0.2" /> | ||||
|     <PackageReference Include="Microsoft.Extensions.Http" Version="9.0.6" /> | ||||
|     <PackageReference Include="Microsoft.Extensions.Http.Polly" Version="9.0.6" /> | ||||
|     <PackageReference Include="System.Text.Encoding.CodePages" Version="9.0.6" /> | ||||
|     <PackageReference Include="ThreeFourteen.Finnhub.Client" Version="1.2.0" /> | ||||
|     <PackageReference Include="YahooFinanceApi" Version="2.3.3" /> | ||||
|   | ||||
| @@ -4,6 +4,8 @@ using Mercados.Infrastructure.DataFetchers; | ||||
| using Mercados.Infrastructure.Persistence; | ||||
| using Mercados.Infrastructure.Persistence.Repositories; | ||||
| using Mercados.Worker; | ||||
| using Polly; | ||||
| using Polly.Extensions.Http; | ||||
|  | ||||
| // Carga las variables de entorno desde el archivo .env en la raíz de la solución. | ||||
| DotNetEnv.Env.Load(); | ||||
| @@ -43,7 +45,27 @@ IHost host = Host.CreateDefaultBuilder(args) | ||||
|  | ||||
|         // El cliente HTTP es fundamental para hacer llamadas a APIs externas. | ||||
|         // Le damos un nombre al cliente de Finnhub para cumplir con los requisitos de su constructor. | ||||
|         services.AddHttpClient("Finnhub"); | ||||
|         //services.AddHttpClient("Finnhub"); | ||||
|          | ||||
|          // Configuramos CADA cliente HTTP que nuestros fetchers usan. | ||||
|         // IHttpClientFactory nos permite nombrar y configurar clientes de forma independiente. | ||||
|          | ||||
|         // Cliente para el scraper del MercadoAgro, con una política de reintentos | ||||
|         services.AddHttpClient("MercadoAgroFetcher") | ||||
|             .AddPolicyHandler(GetRetryPolicy()); | ||||
|  | ||||
|         // Cliente para la API de BCR, con la misma política de reintentos | ||||
|         services.AddHttpClient("BcrDataFetcher") | ||||
|             .AddPolicyHandler(GetRetryPolicy()); | ||||
|              | ||||
|         // Cliente para Finnhub, con la misma política de reintentos | ||||
|         services.AddHttpClient("FinnhubDataFetcher") | ||||
|             .AddPolicyHandler(GetRetryPolicy()); | ||||
|              | ||||
|         // Cliente para YahooFinance (aunque es menos probable que falle, es buena práctica incluirlo) | ||||
|         // La librería YahooFinanceApi usa su propio HttpClient, así que esta configuración | ||||
|         // no le afectará directamente. La resiliencia para YahooFinance la manejaremos de otra forma si es necesario. | ||||
|         // Por ahora, lo dejamos así y nos enfocamos en los que usan IHttpClientFactory. | ||||
|  | ||||
|  | ||||
|         // --- 3. Registro del Worker Principal --- | ||||
| @@ -53,4 +75,23 @@ IHost host = Host.CreateDefaultBuilder(args) | ||||
|     }) | ||||
|     .Build(); | ||||
|  | ||||
| // Esta función define nuestra política de reintentos. | ||||
| static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy() | ||||
| { | ||||
|     // Polly.Extensions.Http nos da este método conveniente. | ||||
|     return HttpPolicyExtensions | ||||
|         // Maneja errores de red transitorios O códigos de estado de servidor que indican un problema temporal. | ||||
|         .HandleTransientHttpError()  | ||||
|         // También maneja el error 408 Request Timeout | ||||
|         .OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.RequestTimeout)  | ||||
|         // Política de reintento con espera exponencial: 3 reintentos, esperando 2^intento segundos. | ||||
|         .WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),  | ||||
|             onRetry: (outcome, timespan, retryAttempt, context) => | ||||
|             { | ||||
|                 // Registramos un log cada vez que se realiza un reintento. | ||||
|                 // Esta es una forma de hacerlo sin tener acceso directo al ILogger aquí. | ||||
|                 Console.WriteLine($"[Polly] Reintentando petición... Intento {retryAttempt}. Esperando {timespan.TotalSeconds}s. Causa: {outcome.Exception?.Message ?? outcome.Result.ReasonPhrase}"); | ||||
|             }); | ||||
| } | ||||
|  | ||||
| await host.RunAsync(); | ||||
		Reference in New Issue
	
	Block a user