C #: поток прерывается без выполнения оставшихся запросов
Я реализовал планировщик, который отправляет запрос на стороннюю конечную точку. После получения ответа моя локальная база данных обновляется ответом. В настоящее время я отправляю более 50 000 запросов (1 000 запросов каждые 10 минут) и обрабатываю ответ. Иногда проблема заключается в том, что сторонний сервер не отвечает или время ожидания запроса истекло. В этом случае я получаю исключение, и поток прерывается без обработки оставшихся запросов. Мне нужно не прерывать поток и переходить к следующей записи, чтобы пропущенная запись обрабатывалась в другом пакете. Вот код, который я использую.
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;
}
}
}
Ответы
Согласно HttpClientдокументации:
HttpClient
предназначен для создания экземпляра один раз для каждого приложения, а не для каждого использования.
HttpClient
экземпляр на запрос может вызвать исчерпание сокетов, что делает невозможным отправку новых запросов.
иногда сторонний сервер не отвечает
Возможно, сторонний сервер в порядке, но ваши сокеты - нет. Это также может вызвать ThreadAbortException
отправку нового запроса, особенно если вы выполняете запрос синхронно. getTask.Wait()
это вызов sync-over-async, который здесь не рекомендуется и не нужен.
Рассмотрите возможность использования этого обновленного кода 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);
}
}
}
Примечание: если вы используете .Result
или .GetAwaiter().GetResult()
не завершили Task
, это означает, что что-то пошло не так, и перед вами плохая практика, которая может вызвать тупик.
Приведенный выше код может быть улучшен для одновременных запросов, например, отправлять все сразу или обрабатывать с максимальным активным пределом сразу. Но сначала лучше убедиться, что приведенный выше код работает.