Подтвердить что ты не робот

В asp.net-mvc, каков правильный способ делать дорогостоящие операции, не влияя на других пользователей?

Я спросил этот вопрос около 5 лет назад о том, как "разгружать" дорогие операции, когда пользователям не нужно ждать (например, auditng и т.д.), поэтому они быстрее получают ответ на лицевой стороне.

Теперь у меня есть связанный, но другой вопрос. На моем asp.net-mvc я создал несколько отчетов, на которых вы можете генерировать отчеты excel (i am using EPPlus) и отчеты PowerPoint (Я использую aspose.slides). Вот пример действия контроллера:

    public ActionResult GenerateExcelReport(FilterParams args)
    {
        byte[] results = GenerateLargeExcelReportThatTake30Seconds(args);
        return File(results, @"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml", "MyReport.xlsx");
    }

Функциональность отлично работает, но я пытаюсь выяснить, влияют ли эти дорогостоящие операции (некоторые отчеты могут занять до 30 секунд) влияют на других пользователей. В предыдущем вопросе у меня была дорогостоящая операция, которую пользователь DIDN "T должен ждать, но в этом случае ему придется ждать в качестве своего синхронного действия (нажмите" Создать отчет "и ожидайте, что пользователи получат отчет, когда его законченный )

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

Есть ли какая-либо передовая практика в asp.net-mvc для этого случая использования?

4b9b3361

Ответ 1

Вы можете попробовать комбинацию Hangfire и SignalR. Используйте Hangfire для запуска фоновой работы и отказа от HTTP-запроса. И как только генерация отчета будет завершена, используйте SignalR для создания push-уведомления.

Уведомление SignalR от сервера к клиенту

Альтернативный вариант - реализовать механизм опроса на стороне клиента. Отправьте вызов ajax, чтобы вставить задание для работы с огнем, чтобы сгенерировать отчет. И затем начните опрос некоторых api, используя другой вызов ajax, который обеспечивает статус, и как только отчет будет готов, извлеките его. Я предпочитаю использовать SignalR вместо опроса.

Если обработка отчета влияет на производительность на веб-сервере, выгрузите эту обработку на другой сервер. Вы можете использовать обмен сообщениями (ActiveMQ или RabbitMQ или какую-либо другую структуру по вашему выбору) или вызов api для отдыха, чтобы начать генерировать отчеты на другом сервере, а затем снова использовать обмен сообщениями или вызов api для уведомления о завершении создания отчетов на веб-сервере, наконец SignalR для уведомления клиента. Это позволит веб-серверу быть более отзывчивым.

UPDATE Что касается вашего вопроса

Есть ли какая-либо передовая практика в asp.net-mvc для этого случая использования

Вы должны следить за сверхурочным приложением. Мониторинг как клиентской, так и серверной. Есть несколько инструментов, на которые можно положиться, таких как newrelic, динамика приложений. Я использовал newrelic, и у него есть функции для отслеживания проблем как в браузере клиента, так и на стороне сервера. Именами продукта являются "NewRelic Browser" и "NewRelic Server". Я уверен, что есть другие инструменты, которые будут захватывать подобную информацию.

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

Здесь вы можете найти некоторую информацию о планировании емкости приложения .net от Microsoft.

-Vinod.

Ответ 2

Все это отличные идеи о том, как переместить работу из цикла запроса/ответа. Но я думаю, что @leora просто хочет знать, будет ли долгосрочный запрос неблагоприятно воздействовать на других пользователей приложения asp.net.

Ответ - нет. asp.net является многопоточным. Каждый запрос обрабатывается отдельным рабочим потоком.

Ответ 3

В целом можно считать хорошей практикой запускать длительные задачи в фоновом режиме и дать какое-то уведомление пользователю, когда работа выполнена. Поскольку вы, вероятно, знаете, что время выполнения веб-запроса ограничено 90 секундами, поэтому, если ваша долговременная задача может превысить это, у вас нет выбора, кроме как запустить какой-либо другой поток/процесс. Если вы используете .net 4.5.2, вы можете использовать HostingEnvironment.QueueBackgroundWorkItem для запуска длинных задач в фоновом режиме и использовать SignalR, чтобы уведомить пользователя, когда задача завершила выполнение. В случае, если вы создаете файл, вы можете сохранить его на сервере с уникальным идентификатором и отправить пользователю ссылку для его загрузки. Вы можете удалить этот файл позже (например, с помощью некоторых служб Windows).

Как упоминалось другими, есть еще несколько продвинутых бегунов фоновых задач, таких как Hangfire, Quartz.Net и другие, но общая концепция - это то же самое - запустить задачу в фоновом режиме и уведомить пользователя, когда это будет сделано. Вот несколько хороших статья о разных приложениях для выполнения фоновых задач.

Ответ 4

Вам нужно использовать async и ждать С#.

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

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

Ответ 5

Я считаю, что инструмент/библиотека, такая как Hangfire - это то, что вы ищете. Во-первых, это позволит вам указать запуск задачи в фоновом потоке (в том же приложении/процессе). Использование различных методов, таких как SignalR, позволяет получать уведомления в режиме реального времени.

Однако то, что я установил после использования Hangfire в течение почти года, разбил нашу работу (и реализацию) на другой сервер, используя эту документацию. Я использую внутреннее приложение ASP.NET MVC для обработки заданий на другом сервере. Таким образом, единственным узким местом производительности является то, что оба сервера используют одно и то же хранилище данных (например, базу данных). Если вы блокируете базу данных, единственный способ ее минимизировать - блокировать указанный ресурс, независимо от используемой вами методологии.

Я использую интерфейсы для запуска заданий, хранящихся в общей библиотеке:

public interface IMyJob
{
    MyJobResult Execute( MyJobSettings settings );
}

И, триггер, найденный в интерфейсном приложении:

//tell the job to run
var settings =  new MyJobSettings();
_backgroundJobClient.Enqueue<IMyJob>( c => c.Execute( settings ) );

Затем на моем фоновом сервере я пишу реализацию (и подключаю его к контейнеру Autofac IOC, который я использую):

public class MyJob : IMyJob
{
    protected override MyJobResult Running( MyJobSettings settings )
    {
         //do stuff here
    }
}

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

Ответ 6

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

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

Ответ 7

Используя этот ответ, вы можете объявить задачу с низким приоритетом

снижение приоритета Task.Factory.StartNew thread

 public ActionResult GenerateExcelReport(FilterParams args)
 {
     byte[] result = null;
     Task.Factory.StartNew(() =>
     {
        result =  GenerateLargeExcelReportThatTake30Seconds(args);
     }, null, TaskCreationOptions.None, PriorityScheduler.BelowNormal)
      .Wait();

     return File(result, @"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml", "MyReport.xlsx");
 }

Ответ 8

Очередь в заданиях в таблице и проведение фонового процесса в этой таблице для определения того, какое очень большое задание нужно запустить дальше. Затем вашему веб-клиенту необходимо будет опросить сервер, чтобы определить, когда задание будет завершено (возможно, проверив флаг в базе данных, но есть и другие методы.) Это гарантирует, что у вас не будет более одного (или сколько бы вы ни были решить, подходит) из этих дорогостоящих процессов, выполняемых одновременно.

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