Фоновое задание Hangfire выдает «Недостаточно памяти для продолжения выполнения программы». - PullRequest
0 голосов
/ 08 марта 2019

Мне трудно разобраться с этим, может быть, кто-то может мне здесь помочь.

У меня есть веб-приложение Asp.net Core 2.0, я использую Hangfire для обработки заданий в фоновом режиме.обычно это синхронизирует продукты или заказы между различными платформами, но есть одно задание, которое всегда терпит неудачу с «Недостаточно памяти для продолжения выполнения программы».а также 'Ссылка на объект не установлена ​​для экземпляра объекта', я записываю трассировку, и она приводит меня к Parallel.ForEach, где выдается ошибка, теперь, когда я делаю то же самое вручную (имеется в виду без зависания), я не получаюисключение и все работает просто отлично.

, поэтому позвольте мне поделиться здесь некоторым кодом.

, поэтому сначала мы настроим Hangfire следующим образомвнутри класса запуска, в методе Configure, мы делаем это.

            app.UseHangfireDashboard("/tasks", new DashboardOptions() {
            Authorization = new[] { new HangFireAuthorizationFilter() }
        });

            var schedules = new ScheduleTasks(app.ApplicationServices);
            schedules.RegisterRecurentJobs();

Теперь ScheduleTasks выглядит следующим образом.

    public class ScheduleTasks
{
    private readonly IApiService _apiService;
    private readonly ISettingService _settingService;

    public ScheduleTasks(IServiceProvider serviceProvider)
    {
        var scope = serviceProvider.CreateScope();
        _apiService = scope.ServiceProvider.GetService<IApiService>();
        _settingService = scope.ServiceProvider.GetService<ISettingService>();
    }

    public void RegisterRecurentJobs()
    {
        var settings = _settingService.LoadSetting<ApiSettings>();

        if(!settings.SilentMode)
        {
            RecurringJob.AddOrUpdate<IApiService>("Config", x => x.SyncConfig(), "* 1 * * *");
            RecurringJob.AddOrUpdate<IApiService>("Manufacturers", x => x.SyncManufacturers(), "* 2 * * *");
            RecurringJob.AddOrUpdate<IApiService>("Products", x => x.SyncProducts(30, false), "* 3 * * *");
        }

    }
}

и вот мой сервисный метод API, который выполнилSyncProducts.

        public async Task SyncProducts(int days, bool hasSalesOnly = true)
    {

        var s = new StringBuilder();
        try
        {
            var search = new SearchModel()
            {
                ModifiedDate = new DateRange()
                {
                    FromDate = DateTime.Today.AddDays(-days)
                },
                PageSize = 300000,
                HaveSalesOnly = hasSalesOnly
            };

            var ids = await _productService.GetProductFeed(search);
            var hasNext = ids?.Result?.AdditionalResultsId;
            var failedIds = new List<string>();
            var retry = true;

            while (ids.Result.Ids != null && ids.Result.Ids.Count > 0)
            {
                var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = 20 };

                s.Append("[");
                var start = true;

                Parallel.ForEach(ids.Result.Ids, parallelOptions, id =>
                {
                    var itemResult = _productService.GetProductByProductId(id).Result;
                    if (itemResult.Success)
                    {
                        lock (s)
                        {
                            if (start)
                            {
                                s.Append(itemResult.ResponseString);
                            }
                            else
                            {
                                var r = "," + itemResult.ResponseString;
                                s.Append(r);
                            }

                            start = false;
                        }
                    }
                    else
                    {
                        failedIds.Add(id);
                    }

                    itemResult = null;

                });

                s.Append("]");

                var newFileName = "result.json";
                var newFileDir = GetJsonFileDirectoryPath() + newFileName;
                var baseDir = Environment.CurrentDirectory + "/wwwroot";
                var dataPath = baseDir + newFileDir;
                File.WriteAllText(dataPath, s.ToString());
                s = null;

                ids.Result.Ids = null;

                var param = new SqlParameter
                {
                    ParameterName = "path",
                    SqlDbType = SqlDbType.VarChar,
                    Direction = ParameterDirection.Input,
                    Value = dataPath
                };

                var imagesToProcess = await _itemRepository.ExecuteStoredProcedureList<SyncItems>("[dbo].[SyncItems]", param);

                foreach (var image in imagesToProcess)
                {
                    if (image.ProductId == null)
                        continue;

                    //for now delete all images, and save the new ones.
                    var imagesToDelete = _pictureService.GetProductPicturesByProductId(image.ProductId.Value);

                    foreach (var i in imagesToDelete)
                    {
                        await _pictureService.DeleteProductPicture(i);
                    }

                    if (image.ImageUrls != null)
                    {
                        var imageList = image.ImageUrls.Split(',');

                        foreach (var i in imageList)
                        {
                            await _pictureService.InsertProductPictureFromPictureUrl(i, image.ProductId.Value);
                        }
                    }


                }

                if (!string.IsNullOrEmpty(hasNext))
                {
                    ids = await _productService.GetNextPage(ids.Result.AdditionalResultsId);
                    hasNext = ids.Result.AdditionalResultsId;
                }
                else
                {
                    ids.Result.Ids = null;
                }

                if (string.IsNullOrEmpty(hasNext) && ids.Result.Ids == null && retry ||
                    string.IsNullOrEmpty(hasNext) && ids.Result.Ids.Count == 0 && retry)
                {
                    ids = new BaseSearchResult();
                    ids.Result.Ids = failedIds;

                    failedIds = new List<string>();
                    retry = false;
                }
            }

            ids = null;

        }
        catch (Exception ex)
        {
            s = null;
            aEvent = new ApiEvent()
            {
                Level = LogLevel.Critical,
                EventDate = DateTime.Now,
                User = "Api",
                EventType = EventTypeEnum.Product,
                EntityTypeId = "",
                Event = ex.Message,
                Details = ex.StackTrace
            };

            await _eventService.PublishEvent(aEvent);
        }

    }

Если я выполняю этот метод вручную, либо путем написания функционального теста для выполнения метода, либо с помощью кнопки на моей странице, которая вызывает выполнение этого метода, все работает нормально, но когдаHangfire пытается выполнить точно такой же метод, я получаю эту ошибку.

Произошла одна или несколько ошибок.(Недостаточно памяти для продолжения выполнения программы.) (Ссылка на объект не установлена ​​для экземпляра объекта.) (Ссылка на объект не установлена ​​для экземпляра объекта.) (Ссылка на объект не установлена ​​на экземпляр объекта.) (Ссылка на объект не установлена ​​на экземпляр объекта.) (Ссылка на объект не установлена ​​на экземпляр объекта.) (Ссылка на объект не установлена ​​на экземпляр объекта.) (Ссылка на объект не установлена ​​на экземпляр объектаobject.) (Ссылка на объект не установлена ​​на экземпляр объекта.) (Ссылка на объект не установлена ​​на экземпляр объекта.) (Ссылка на объект не установлена ​​на экземпляр объекта.) (Ссылка на объект не установлена ​​на экземпляробъекта.) (Ссылка на объект не установлена ​​для экземпляра объекта.) (Ссылка на объект не установлена ​​на экземпляр объекта.) (Ссылка на объект не установлена ​​на экземпляр объекта.) (Ссылка на объект не установлена ​​наэкземпляр объекта.) (Ссылка на объект не установлена ​​на экземпляр объекта.) (Ссылка на объект не установлена ​​на экземпляробъекта.) (Ссылка на объект не установлена ​​для экземпляра объекта.)

и трассировка такова.

в System.Threading.Tasks.TaskReplicator.Run [TState] (ReplicatableUserAction 1 action, ParallelOptions options, Boolean stopOnFirstFailure) at System.Threading.Tasks.Parallel.ForWorker[TLocal](Int32 fromInclusive, Int32 toExclusive, ParallelOptions parallelOptions, Action 1 body, Action 2 bodyWithState, Func 4 bodyWithLocal, Func 1 localInit, Action 1 localFinally) --- Конец трассировки стека из предыдущего расположения, в котором было сгенерировано исключение --- в System.Threading.TasksКласс, Список действий 1 localFinally) at System.Threading.Tasks.Parallel.ForEachWorker[TSource,TLocal](IList 1, Параллельные параметры ParallelOptions, Действия 1 body, Action 2 bodyWithState, Действие 3 bodyWithStateAndIndex, Func 4 bodyWithStateAndLocal, Func 5 bodyWithEverything, Func 1 localInit, Источник действия 1 localFinally) at System.Threading.Tasks.Parallel.ForEachWorker[TSource,TLocal](IEnumerable 1, Параллельные параметры ParallelOptions, Действие 1 body, Action 2 bodyWithState, Действие3 bodyWithStateAndIndex, Func 4 bodyWithStateAndLocal, Func 5 bodyWithEverything, Func 1 localInit, Action 1 localFinally) at System.Threading.Tasks.Parallel.ForEach[TSource](IEnumerable 1 source, ParallelOptions parallelOptions, тело Action`1) в *****. *****. Services.ApiService.SyncProducts (Int32 дней, логическое значение hasSalesOnly) в C: ***************. **** \ Services \ ApiService.cs: строка 493

Может кто-нибудь помочь мне понять, почему происходит сбой при выполнении метода hangfire?

Заранее спасибо завсе смотрят на это.

...