C#:线程中止而未完成剩余请求

问题描述

我已经实现了将请求发送到第三方端点的调度程序。收到响应后,将使用响应更新本地数据库。目前,我发送了5万多个请求(每10分钟1000个请求)并处理响应。问题有时是第三方服务器没有响应或请求超时。在这种情况下,我得到一个异常,线程被中止而没有处理剩余的请求。我需要的是不要中止线程并继续处理下一条记录,以便错过的记录在另一批中处理。这是我正在使用的代码。

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文档:

<app-modal [modalTitle]="modalTitle" [modalId]="modalId" (closeModal)="close.emit(false)"> <ng-container *ngIf="results; else loading"> <div class="modal modal-first-view"> <div class="modal-text modal-text-first-view" [innerHtml]="result.welcomePopupBody"> </div> <div class="first-view-button-wrap"> <button (click)="buttonClicked()" type="button" class="button button-double-shadow button-first-view"> {{ result.welcomePopupButtonText }} </button> </div> <div class="modal-img modal-img-first-view first-view-lady"> <img src="/assets/img/Woman-coach.png" alt="Nice lady here to help" /> </div> </div> </ng-container> <ng-template #loading> ...Loading </ng-template> </app-modal> 旨在针对每个应用实例化一次,而不是针对每次使用实例化。

每个请求

HttpClient实例可能导致Socket Exhaustion无法发送新请求。

有时第三方服务器没有响应

也许第三方服务器还可以,但您的套接字却不行。它还可能导致在发送某些新请求时发送HttpClient,尤其是在同步运行请求的情况下。 ThreadAbortException是异步同步调用,不建议使用,这里也不需要。

考虑此更新的代码以使用getTask.Wait()

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(),则表示出了点问题,并且前面有不良做法可能会导致死锁。

上面的代码可以改进为并发请求,例如一次发送所有邮件,或一次处理最大活动限制。但是首先要确保上述代码有效。

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...