В asp.net-mvc, как я могу выполнить дорогостоящую операцию, не замедляя работу пользователя? - PullRequest
21 голосов
/ 06 июня 2011

У меня есть веб-сайт asp.net-mvc, и я использую nhibernate для своего ORM.

У меня есть текущее действие контроллера, которое выполняет базовое обновление CRUD (запрашивает элемент из базы данных, а затем обновляеткуча значений и фиксирует обратно в таблицу БД).Затем он возвращает простой ответ json клиенту, чтобы указать успех или ошибку.

 public ActionResult UpdateEntity(MyEntity newEntity)
 {
      var existingEntity = GetFromRepository(newEntity.Id);
      UpdateExistingEntity(newEntity, existingEntity);
      return Json(SuccessMessage);
 }

В некоторых случаях (при условии успешной фиксации и изменения определенных полей в моем объекте) я теперь хочу вызвать некоторые дополнительныедействия (например, отправка писем по электронной почте нескольким людям и запуск кода, генерирующего отчет), но я не хочу замедлять работу пользователя, который выполняет обновление.Поэтому я обеспокоен тем, что если бы я сделал это:

 public ActionResult UpdateEntity(MyEntity newEntity)
 {
      var existingEntity = GetFromRepository(newEntity.Id);
      bool keyFieldsHaveChanged = UpdateExistingEntity(newEntity, existingEntity);
      if (keyFieldsHaveChanged)
     {
          GenerateEmails();
          GenerateReports();
     }
    return Json(SuccessMessage);
 }

, то это было бы слишком медленно для пользовательского опыта обновления.Есть ли в любом случае (asyngc?) Запуск дорогостоящей операции из-за действия контроллера, но не из-за этого замедленного действия контроллера?

Ответы [ 9 ]

51 голосов
/ 06 июня 2011

Я делал это раньше.

Самый надежный способ - использовать асинхронный контроллер или, что еще лучше, независимую службу, такую ​​как служба WCF.

Но по моему опыту, мне просто нужно было выполнить "простую" задачу, состоящую из одной строки, такую ​​как аудит или отчетность, как вы говорите.

В этом примере самый простой способ - выстрелить из Task:

public ActionResult Do()
{
    SomethingImportantThatNeedsToBeSynchronous();

    Task.Factory.StartNew(() => 
    {
       AuditThatTheUserShouldntCareOrWaitFor();
       SomeOtherOperationTheUserDoesntCareAbout();
    });

    return View();

}

Это простой пример. Вы можете запускать столько задач, сколько хотите, синхронизировать их, получать уведомления об их завершении и т. Д.

В настоящее время я использовал вышеперечисленное для загрузки на Amazon S3.

16 голосов
/ 01 сентября 2011

Если намерение состоит в том, чтобы немедленно вернуть JSON и оставить интенсивную работу для асинхронного выполнения в фоновом режиме, не затрагивая пользовательский опыт, то вам нужно запустить новый фоновый поток.AsyncController здесь вам не поможет.

Существует множество аргументов в пользу истощения пула запросов потоков, выполняя это (что верно), но аргумент счетчика заключается в том, что вы должны голодать пулом, поскольку сервер занят выполнениемРабота.Конечно, в идеале вы должны перенести работу на другой сервер полностью через системы очередей / распределенные системы и т. Д., Но это сложное решение.Если вам не нужно обрабатывать сотни запросов, вам не нужно рассматривать эту опцию, поскольку вряд ли это когда-либо вызовет проблему.Это действительно зависит от требований к масштабируемости вашего решения, сколько времени займет фоновый процесс и как часто он вызывается.Самое простое решение заключается в следующем:

public ActionResult UpdateEntity(MyEntity newEntity)
{
    var existingEntity = GetFromRepository(newEntity.Id);
    bool keyFieldsHaveChanged = UpdateExistingEntity(newEntity, existingEntity);
    if (keyFieldsHaveChanged)
    {
        ThreadPool.QueueUserWorkItem(o =>
                                        {
                                            GenerateEmails();
                                            GenerateReports();
                                        });

    }
    return Json(SuccessMessage);
}
5 голосов
/ 06 июня 2011

Вы должны выполнять асинхронные операции и асинхронные контроллеры, чтобы не блокировать пул потоков и не причинять страдания другим пользователям веб-сайта.Когда задача выполняется долго, поток, взятый из пула потоков asp.net, зарезервирован и не возвращается в пул до завершения операции.Если будет много одновременно выполняемых задач, многие потоки будут зарезервированы ими, так что есть большой шанс, что другие пользователи, которые посещают ваш сайт, пострадают от ожидания.ОПЕРАЦИИ ASYNC НЕ БУДУТ СДЕЛАТЬ ЛЮБОГО КОДА.Я советую вам использовать асинхронные контроллеры только для потоков, о которых я писал выше, но этого недостаточно.Я думаю, что вы должны использовать некоторые ссылки или ajax для запуска работы на сервере и позволить пользователю продолжить просмотр сайта.После завершения операции при обновлении следующей страницы пользователь должен получить уведомление о завершении выполнения задачи.И вот еще одно доказательство того, что бизнес-коды не должны быть записаны в контроллере.Для этого вам нужно было раздельные услуги.

3 голосов
/ 26 марта 2015

Это старый вопрос, поэтому я считаю, что он нуждается в обновлении.

Я рекомендую использовать HangFire (http://hangfire.io).. При этом вы можете просто поставить свою работу в очередь даже в веб-приложении . HangFire обеспечит выполнение задания хотя бы один раз.

// Static methods are for demo purposes
BackgroundJob.Enqueue(
    () => Console.WriteLine("Simple!"));

Вы также можете просматривать статус всех заданий в очереди в хорошем интерфейсе.

2 голосов
/ 29 августа 2011

Я думаю, что вам действительно нужна рабочая роль, похожая на Windows Azure.

http://www.microsoft.com/windowsazure/features/compute/

Я не уверен, как лучше всего реализовать это в чистом MVC без очереди Azure. И, в зависимости от того, где вы размещаете хостинг (интернет-хостинг, ваш собственный сервер с root и т. Д.), Могут возникнуть сложности.

1 голос
/ 04 сентября 2011

Если вы думаете о пользовательском опыте (пользователь не должен ждать, пока эти задачи будут выполнены) и надежности (задачи должны быть поставлены в очередь и выполнены, даже если приложение перезапустится), вы можете использовать MSMQ.

MSMQ предоставляет надежное решение для операций асинхронизации.Он поддерживает операции синхронизации, предоставляет журналы и управляемый API, и в качестве основного внимания уделяет ситуации такого рода.

Посмотрите следующие статьи:

Введение в MSMQ

Программирование MSMQ в .Net

1 голос
/ 04 сентября 2011

Это в основном не асинхронно, поскольку я правильно прочитал ваш вопрос. Это о длительной операции. Вы должны перенести длительную операцию в фоновые задания. Приложения ASP.NET не подходят для выполнения фоновых заданий. Я вижу несколько вариантов, с которыми вы можете пойти:

  1. Служба Windows - может опрашивать вашу БД на предмет определенного состояния и запускать действие с этого состояния и далее
  2. Служба WCF - ваше приложение ASP.NET может отправлять асинхронный запрос службе WCF, не ожидая ответа
  3. Могут быть и другие, такие как BizTalk, но это зависит от структуры вашего приложения и т. Д.

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

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

1 голос
/ 01 сентября 2011

Используя идею @IKEA Riot службы Windows и флага базы данных, вы можете использовать что-то вроде Quartz.Net или Castle.Scheduler , который интегрируется в ваш сайтили превратиться в отдельную службу Windows, которая может запускать определенные задания.

Stackoverflow - кварцевая сетка с сеткой из аспирата

0 голосов
/ 05 сентября 2011

Используйте служебную шину для публикации сообщения при обновлении объекта. Таким образом, вы не только избежите длительных операций в контроллерах, но также сможете отделить контроллеры от действий, которые необходимо выполнить, когда что-то случится.

Вы также можете использовать События домена для обработки действий и объединения их с вашим любимым контейнером ioc: Advanced StructureMap: подключение реализаций для открытия универсальных типов

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...