Squeak.ru - шаблоны программирования

Веб-задания Azure с параллелизмом заданий Entity Framework

У меня есть веб-задание Azure, развернутое с помощью моего веб-приложения MVC Azure. Это задание имеет две функции, запускаемые очередью: одна берет PDF-файл и разбивает его на отдельные изображения страниц, а вторая обрабатывает отдельные страницы. Я использую EF 6 и Unity как на веб-сайте, так и в веб-работе.

Используя параметр IJobActivator, у меня нет проблем с внедрением моего DbContext и UoW/Services/Repositories в веб-задание. Проблема возникает, когда я пытаюсь запустить веб-задание параллельно. Если я установлю для параметров конфигурации веб-задания значение config.Queues.BatchSize = 1, все будет хорошо, но медленно (последовательно, по одной странице за раз). Если я увеличу BatchSize до значения выше 1, я получу ошибки в строке «Вторая операция, начатая в этом контексте, до завершения предыдущей асинхронной операции». Поскольку DbContext вводится, я пытался возиться с параметрами LifetimeManager, чтобы увидеть, была ли проблема в этом - без изменений. В ошибке говорится, что мне нужно убедиться, что все мои вызовы EF используют «ожидание» (асинхронные), но они ... так что это не проблема.

Полезность веб-задания серьезно уменьшится, если я не смогу запускать его параллельно. есть идеи? Было бы лучше отказаться от EF и DbContext в веб-задании и просто использовать ADO.Net для прямого доступа к БД?


Ответы:


1

Сообщение об ошибке ясно указывает на то, что вы не можете одновременно выполнять несколько невыполненных асинхронных операций с одним и тем же DbContext. Поскольку вы определенно хотите поддерживать параллелизм своей работы (т. Е. Не устанавливайте BatchSize равным 1), вам, вероятно, следует просто использовать один новый DbContext для каждого вызова функции задания. DbContext легкий, поэтому это не должно быть проблемой для вас.

Кроме того, официальная документация DbContext предлагает следующие рекомендации по управлению экземплярами DbContext в WebApps. : "При работе с веб-приложениями используйте экземпляр контекста для каждого запроса". Это руководство применимо к вашему случаю веб-задания.

09.12.2015
  • Ваш комментарий заставил меня задуматься о том, что я делаю. Я запускал веб-задание локально в режиме отладки через VS (но разговаривал с живыми очередями хранилища, поскольку триггеры очереди нельзя эмулировать). Возможно, именно это вызывает проблему, поскольку на самом деле работа не выполняется в настоящей среде веб-приложения. Я изменил конфигурацию единства на PerRequestLifetimeManager и собираюсь опубликовать на Azure, чтобы посмотреть, как это работает. 10.12.2015
  • Итак, когда я переместил это в настоящую Azure, я столкнулся с отдельной проблемой (возможно, которую нужно задать отдельно). Веб-приложение работает нормально, и БД работает нормально. Как только WebJob запускается и обращается к БД, база данных уничтожается. БД есть, но удаляются ВСЕ таблицы/данные и начинают слетать исключения. Я восстановил БД и попробовал снова, тот же результат. Мой инициализатор DbContext имеет значение null, поэтому он не должен пытаться делать что-либо в этом направлении. Мысли? 10.12.2015
  • Ну, я бы сказал, дважды проверьте свой код WebJob, что вы случайно не используете инициализатор - не так много других вещей, которые могут стирать вашу БД таким образом. Вы говорите, что у вас есть явный вызов Database.SetInitializer<T>(null) для вашего контекста? Вы также можете запустить WebJob локально и использовать VS intellitrace, чтобы увидеть, какие операторы выполняются, когда. 10.12.2015
  • Да, у меня есть явный вызов Database.SetInitializer‹T›(null) в моем конструкторе контекста. При локальном запуске все работает, кроме исходной проблемы с параллельным доступом и связанных с ней исключений. Я вернусь к локальному запуску и посмотрю, что найдет inellitrace. 10.12.2015
  • Мне нужно было сохранить жизненный цикл DbContext в конкретном методе WebJob. До того, как я использовал DI и контейнер Unity, а внедрение происходило выше, я думаю, что это было проблемой. Я избавился от этого, сделал оператор using непосредственно в DbContext и напрямую создал экземпляры сервисов и репозиториев внутри конкретного метода WebJob. Теперь, по крайней мере локально, все работает идеально, даже при запуске множества параллельных экземпляров WebJob. ОДНАКО, в Azure все по-прежнему взрывается, потому что БД ВСЕ ЕЩЕ сбрасывается при запуске задания. Я не могу запустить Intellitrace, потому что у меня нет Enterprise. 10.12.2015

  • 2

    Как упоминалось в моем комментарии, решение проблемы параллелизма сводится к сохранению времени жизни DbContext в пределах одного метода WebJob, поскольку EF не является потокобезопасным. Попытка использовать Unity и IJobActivator сохранила эти вещи на более высоком уровне (вне метода), и даже изменение LifetimeManager не дало никакого эффекта. Избавившись от Unity и просто создав оператор «using» внутри WebJob в DbContext, он сохранил все в безопасности и работал.

    Потеря/сброс базы данных SQL в рабочей среде Azure, по-видимому, связана с инициализаторами и миграциями базы данных EF. Я собираюсь начать еще один вопрос по этому поводу, поскольку он не имеет прямого отношения к моему первоначальному вопросу.

    10.12.2015

    3

    Как вы настроили регистрацию в Unity? Я думаю, что если вы хотите внедрить свой DbContext, вам следует просто использовать регистрацию, которая создает новый экземпляр для каждого вызова. Каждый раз, когда в вашу очередь поступает новое сообщение, платформа будет использовать IJobActivator для получения зависимостей.

    У меня есть реализация IJobActivator с использованием Simple Injector.

    /// <summary>
    /// An activator that uses <see cref="SimpleInjector"/> to return instance of a job type.
    /// </summary>
    public class SimpleInjectorJobActivator : IJobActivator
    {
        /// <summary>
        /// Gets or sets the container to resolve dependencies.
        /// </summary>
        private readonly Container _container;
    
        /// <summary>
        /// Initialize a new instance of the <see cref="SimpleInjectorJobActivator"/> class.
        /// </summary>
        /// <param name="container">The <see cref="Container"/> to resolve dependencies.</param>
        public SimpleInjectorJobActivator(Container container)
        {
            _container = container;
        }
    
        /// <summary>
        /// Creates a new instance of a job type.
        /// </summary>
        /// <typeparam name="T">The job type.</typeparam>
        /// <returns>
        /// A new instance of the job type.
        /// </returns>
        public T CreateInstance<T>()
        {
            return (T)_container.GetInstance(typeof(T));
        }
    }
    

    Если вы создаете обработчик заданий для каждого триггера очереди, вы можете создать два таких класса:

    public class MyJobProcessor1
    {
        private readonly MyDbContext _myDbContext;
    
        public MyJobProcessor1(MyDbContext myDbContext)
        {
            _myDbContext = myDbContext;
        }
    
    
        public void Run([QueueTrigger("my-queue1")] BrokeredMessage message)
        {
    
        }
    }
    

    И моя основная функция:

    static void Main()
    {
        // Register my dependency, by default simple injector create a new instance per call
        var container = new Container();
        container.Register(() => new MyDbContext());
        container.Verify();
    
        // Configure the job host
        var config = new JobHostConfiguration
        {
            JobActivator = new SimpleInjectorJobActivator(container)
        };
        // Run the host
        var host = new JobHost(config);
        host.RunAndBlock();
    }
    

    Каждый раз, когда приходит новое сообщение, IJobActivator возвращает новый экземпляр DbContext, зарегистрированный в моем контейнере IoC.

    14.12.2015
    Новые материалы

    Угловая структура архитектуры
    Обратите внимание, что эта статья устарела, я решил создать новую с лучшей структурой и с учетом автономных компонентов: https://medium.com/@marekpanti/angular-standalone-architecture-b645edd0d54a..

    «Данные, которые большинство людей используют для обучения своих моделей искусственного интеллекта, поставляются со встроенным…
    Первоначально опубликовано HalkTalks: https://hacktown.com.br/blog/blog/os-dados-que-a-maioria-das-pessoas-usa-para-treinar-seus-modelos-de-inteligencia-artificial- ja-vem-com-um-vies-embutido/..

    Сильный ИИ против слабого ИИ: различия парадигм искусственного интеллекта
    В последние годы изучению и развитию искусственного интеллекта (ИИ) уделяется большое внимание и прогресс. Сильный ИИ и Слабый ИИ — две основные парадигмы в области искусственного интеллекта...

    Правильный способ добавить Firebase в ваш проект React с помощью React Hooks
    React + Firebase - это мощная комбинация для быстрого и безопасного создания приложений, от проверки концепции до массового производства. Раньше (знаете, несколько месяцев назад) добавление..

    Создайте API с помощью Python FastAPI
    Создание API с помощью Python становится очень простым при использовании пакета FastAPI. После установки и импорта вы можете создать приложение FastAPI и указать несколько конечных точек. Каждой..

    Веселье с прокси-сервером JavaScript
    Прокси-серверы JavaScript — это чистый сахар, если вы хотите создать некоторую общую логику в своих приложениях, чтобы облегчить себе жизнь. Вот один пример: Связь клиент-сервер Мы..

    Получить бесплатный хостинг для разработчиков | Разместите свой сайт за несколько шагов 🔥
    Статические веб-сайты — это веб-страницы с фиксированным содержанием и его постоянным содержанием. Но теперь статические сайты также обрабатывают динамические данные с помощью API и запросов...