C # : 나머지 요청을 완료하지 않고 스레드가 중단됨

Aug 18 2020

타사 엔드 포인트에 요청을 보내는 스케줄러를 구현했습니다. 응답을받은 후 내 로컬 데이터베이스가 응답으로 업데이트됩니다. 현재 5 만 건 이상의 요청 (10 분마다 1K 요청)을 보내고 응답을 처리하고 있습니다. 문제는 때때로 타사 서버가 응답하지 않거나 요청 시간이 초과되는 것입니다. 이 경우 예외가 발생하고 나머지 요청을 처리하지 않고 스레드가 중단됩니다. 내가 필요한 것은 스레드를 중단하고 다음 레코드로 이동하여 놓친 레코드가 다른 배치에서 처리되도록하는 것입니다. 내가 사용하고있는 코드는 다음과 같습니다.

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요청 당 인스턴스는 새 요청을 보낼 수없는 소켓 소모 를 유발할 수 있습니다 .

때때로 타사 서버가 응답하지 않습니다.

타사 서버는 괜찮지 만 소켓은 그렇지 않습니다. 또한 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뭔가 잘못되었으며 교착 상태를 유발할 수있는 나쁜 습관이 앞에 있음을 의미합니다.

위의 코드는 동시 요청으로 개선 될 수 있습니다 (예 : 한 번에 모두 보내기 또는 한 번에 최대 활성 제한으로 처리). 그러나 먼저 위의 코드가 작동하는지 확인하는 것이 좋습니다.