C #: เธรดยกเลิกโดยไม่ต้องดำเนินการตามคำขอที่เหลือ

Aug 18 2020

ฉันใช้ตัวกำหนดตารางเวลาที่ส่งคำขอไปยังปลายทางของบุคคลที่สาม หลังจากได้รับการตอบกลับฐานข้อมูลในเครื่องของฉันจะได้รับการอัปเดตด้วยการตอบกลับ ขณะนี้ฉันส่งคำขอมากกว่า 50K คำขอ (คำขอ 1K ทุกๆ 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;
        }
    }
}

คำตอบ

aepot Aug 18 2020 at 14:38

ตามHttpClientเอกสาร:

HttpClient มีวัตถุประสงค์เพื่อสร้างอินสแตนซ์หนึ่งครั้งต่อแอปพลิเคชันแทนที่จะเป็นแบบต่อการใช้งาน

HttpClientอินสแตนซ์ต่อคำขออาจทำให้เกิดSocket Exhaustionซึ่งทำให้ไม่สามารถส่งคำขอใหม่ได้

บางครั้งเซิร์ฟเวอร์ของบุคคลที่สามไม่ตอบสนอง

เซิร์ฟเวอร์ของบุคคลที่สามอาจจะใช้ได้ แต่ Sockets ของคุณไม่เป็นเช่นนั้น นอกจากนี้ยังอาจทำให้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หมายความว่ามีบางอย่างผิดพลาดและมีการปฏิบัติที่ไม่ดีต่อหน้าคุณซึ่งอาจทำให้เกิดการชะงักงัน

โค้ดข้างต้นสามารถปรับปรุงเป็นคำขอพร้อมกันได้เช่นส่งทั้งหมดพร้อมกันหรือจัดการกับขีด จำกัด สูงสุดที่ใช้งานได้ในครั้งเดียว แต่ก่อนอื่นจะดีกว่าเพื่อให้แน่ใจว่าโค้ดข้างต้นทำงานได้