C #: Thread dibatalkan tanpa menyelesaikan permintaan yang tersisa
Saya telah menerapkan penjadwal yang mengirimkan permintaan ke titik akhir pihak ketiga. Setelah menerima tanggapan, database lokal saya diperbarui dengan tanggapan tersebut. Saat ini, saya mengirim lebih dari 50 ribu permintaan (1 ribu permintaan setiap 10 menit) dan memproses tanggapannya. Masalahnya terkadang server pihak ketiga tidak merespons atau waktu permintaan habis. Dalam hal ini saya mendapatkan pengecualian dan utas dibatalkan tanpa memproses permintaan yang tersisa. Yang saya butuhkan bukanlah membatalkan utas dan melanjutkan dengan rekaman berikutnya sehingga rekaman yang terlewat diproses dalam batch lain. Ini kode yang saya gunakan.
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;
}
}
}
Jawaban
Sesuai HttpClientdokumentasi:
HttpClient
dimaksudkan untuk digunakan sekali per aplikasi, bukan per penggunaan.
HttpClient
contoh per permintaan dapat menyebabkan Socket Exhaustion yang tidak memungkinkan untuk mengirim permintaan baru.
terkadang server pihak ketiga tidak merespons
Mungkin server pihak ketiga baik-baik saja tetapi Soket Anda tidak. Ini juga dapat menyebabkan ThreadAbortException
beberapa permintaan baru dikirim terutama jika Anda menjalankan permintaan secara sinkron. getTask.Wait()
adalah panggilan sync-over-async yang tidak disarankan dan tidak diperlukan di sini.
Pertimbangkan kode yang diperbarui ini untuk digunakan 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);
}
}
}
Catatan: jika Anda menggunakan .Result
atau .GetAwaiter().GetResult()
untuk tidak selesai Task
, berarti ada yang tidak beres dan ada praktik buruk di depan Anda yang mungkin menyebabkan kebuntuan.
Kode di atas dapat ditingkatkan menjadi permintaan bersamaan misalnya mengirim semua sekaligus atau menangani dengan batas aktif maksimum sekaligus. Tetapi pertama-tama lebih baik untuk memastikan bahwa kode di atas berfungsi.