C #: Thread abandonne sans terminer les demandes restantes
J'ai implémenté un planificateur qui envoie une demande à un point de terminaison tiers. Après avoir reçu la réponse, ma base de données locale est mise à jour avec la réponse. Actuellement, j'envoie plus de 50 000 demandes (1 000 demandes toutes les 10 minutes) et je traite la réponse. Le problème est parfois que le serveur tiers ne répond pas ou que la demande expire. Dans ce cas, j'obtiens une exception et le thread est abandonné sans traiter les demandes restantes. Ce dont j'ai besoin est de ne pas abandonner le thread et de passer à l'enregistrement suivant afin que l'enregistrement manqué soit traité dans un autre lot. Voici le code que j'utilise.
public class ScheduledAPIJob : IJob
{
public Task Execute(IJobExecutionContext context)
{
Task taskAPI = Task.Factory.StartNew(() => ProcessAPI());
return taskAPI;
}
void ProcessAPI()
{
//Error logging object
SchedulerLogWriter lw = new SchedulerLogWriter("Logs\\Scheduler");
List<WeatherData> list = new List<WeatherData>();
APIQueueBAL objBal = new APIQueueBAL();
//List of endpoints to hit.
var APIQueue = objBal.QueuedAPIs();
foreach (var item in APIQueue)
{
try
{
var endpoint = item.FunctionParameters;
HttpRequestHelper objRequestHelper = new HttpRequestHelper();
//Response from API
var response = objRequestHelper.GetAPIResponse(endpoint);
////Update local database.
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
list = JsonConvert.DeserializeObject<List<WeatherData>>(response.Content.ReadAsStringAsync().Result);
objBal.ProcessWeatherData(item, list);
}
}
catch (Exception ex)
{
lw.WriteLog(ex.Message);
lw.WriteLog(Convert.ToString(ex.InnerException));
lw.WriteLog(ex.StackTrace);
}
}
}
}
public class HttpRequestHelper
{
public HttpResponseMessage GetAPIResponse(string apiEndpoint)
{
using (var client = new HttpClient())
{
var getTask = client.GetAsync(apiEndpoint);
getTask.Wait();
return getTask.Result;
}
}
}
Réponses
Selon la HttpClientdocumentation:
HttpClient
est destiné à être instancié une fois par application, plutôt que par utilisation.
HttpClient
instance par requête peut entraîner un épuisement du socket qui ne permet pas d'envoyer de nouvelles requêtes.
parfois le serveur tiers ne répond pas
Peut-être que le serveur tiers est OK, mais vos sockets ne le sont pas. Cela peut également entraîner l' ThreadAbortException
envoi d'une nouvelle demande, en particulier si vous exécutez la demande de manière synchrone. getTask.Wait()
est un appel sync-over-async qui n'est ni recommandé ni nécessaire ici.
Considérez ce code mis à jour à utiliser async/await
.
public class ScheduledAPIJob : IJob
{
public Task Execute(IJobExecutionContext context)
{
return ProcessAPI();
}
private async Task ProcessAPI()
{
//Error logging object
SchedulerLogWriter lw = new SchedulerLogWriter("Logs\\Scheduler");
APIQueueBAL objBal = new APIQueueBAL();
//List of endpoints to hit.
var APIQueue = objBal.QueuedAPIs();
foreach (var item in APIQueue)
{
try
{
string endpoint = item.FunctionParameters;
//Response from API
List<WeatherData> list = await HttpRequestHelper.GetAPIResponseAsync<List<WeatherData>>(endpoint);
objBal.ProcessWeatherData(item, list);
}
catch (Exception ex)
{
lw.WriteLog(ex.Message);
lw.WriteLog(Convert.ToString(ex.InnerException));
lw.WriteLog(ex.StackTrace);
}
}
}
}
public static class HttpRequestHelper
{
private static readonly HttpClient client = new HttpClient();
public static async Task<T> GetAPIResponseAsync<T>(string apiEndpoint)
{
using (HttpResponseMessage response = await client.GetAsync(apiEndpoint, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false))
{
response.EnsureSuccessStatusCode(); // throws if not success
string json = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
return JsonConvert.DeserializeObject<T>(json);
}
}
}
Remarque: si vous utilisez .Result
ou .GetAwaiter().GetResult()
non terminé Task
, cela signifie que quelque chose s'est mal passé et qu'il y a une mauvaise pratique devant vous qui peut provoquer une impasse.
Le code ci-dessus peut être amélioré pour les demandes simultanées, par exemple envoyer tout en une seule fois ou gérer avec une limite active maximale à la fois. Mais avant tout, il est préférable de s'assurer que le code ci-dessus fonctionne.