ASP.NET (MVC) Outputcache и параллельные запросы - PullRequest
9 голосов
/ 29 января 2010

Допустим, теоретически, у меня на сайте есть действие «страница / контроллер», которое делает очень тяжелые вещи. Для завершения операции требуется около 10 секунд.

Теперь я использую механизм выходного кэша .NET, чтобы кэшировать его в течение 15 минут (например, я использую [OutputCache(Duration = 900)]). Что произойдет, если через 15 минут кеш истечет и 100 пользователей снова запросят страницу в течение этих 10 секунд что требуется, чтобы сделать тяжелую обработку?

  1. Тяжелая работа выполняется только в первый раз, и есть некоторый механизм блокировки, чтобы остальные 99 пользователей получили результат кеша
  2. Тяжелая работа выполняется 100 раз (и сервер отключается, так как это может занять до 100 * 10 секунд)

Простой вопрос, может быть, но я не уверен на 100%. Я надеюсь, что это номер один, хотя: -)

Спасибо!

Ответы [ 5 ]

4 голосов
/ 29 января 2010

Ну, это зависит от того, как у вас настроен IIS. Если у вас менее 100 рабочих потоков (скажем, 50), то «тяжелая работа» выполняется 50 раз, нанося вред вашему серверу, а затем оставшиеся 50 запросов будут обслуживаться из кэша.

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

Редактировать : Я верю, что это правда, но тесты Ника говорят иначе, и у меня нет времени сейчас проверять. Попробуй сам! Остальная часть ответа не зависит от вышесказанного, и я думаю, что это более важно.

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

Вы могли бы также захотеть рассмотреть асинхронные контроллеры . Наконец, обратите внимание, что, хотя IIS и ASP.NET MVC не будут блокировать эти сложные вычисления, вы можете это сделать. Если вы используете асинхронные контроллеры в сочетании с блокировкой вычислений, то вы получите эффективное поведение, о котором вы просили. Я не могу точно сказать, является ли это лучшим решением, не зная больше о том, что вы делаете.

3 голосов
/ 11 декабря 2012

Старый вопрос, но я столкнулся с этой проблемой и провел некоторое расследование.

Пример кода:

public static int Count;
[OutputCache(Duration = 20, VaryByParam = "*")]
public ActionResult Test()
{
    var i = Int32.MaxValue;
    System.Threading.Thread.Sleep(4000);
    return Content(Count++);
}

Запустите его в одном браузере, и он, кажется, блокируется и ждет.

Запустите его в разных браузерах (я тестировал в IE и Firefox), и запросы не удерживаются.

Таким образом, «правильное» поведение больше связано с тем, какой браузер вы используете, чем с функцией в IIS.

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

3 голосов
/ 29 января 2010

Кажется, здесь можно выполнить простой тест:

<%@ OutputCache Duration="10" VaryByParam="*" %>

protected void Page_Load(object sender, EventArgs e)
{
    System.Threading.Thread.Sleep(new Random().Next(1000, 30000));
}

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

Примечание: это было проще тестировать в сценарии веб-форм, но, учитывая, что это общий аспект фреймворков, вы можете выполнить тот же тест в MVC с тем же результатом.

Вот альтернативный способ проверки:

<asp:Literal ID="litCount" runat="server" />

public static int Count = 0;

protected void Page_Load(object sender, EventArgs e)
{
  litCount.Text = Count++.ToString();
  System.Threading.Thread.Sleep(10000);
}

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

1 голос
/ 25 мая 2011

Я сделал небольшой тест, который может помочь.Я полагаю, что я обнаружил, что не кэшированные запросы не блокируются, и каждый запрос, который приходит в то время, когда срок действия кэша истек и до того, как задача завершена, ТАКЖЕ запускает эту задачу.занимает около 6-9 секунд в моей системе с использованием Cassini.Если вы отправите два запроса с интервалом примерно 2 секунды (т.е. две вкладки браузера), оба получат уникальные результаты.Последний запрос на завершение также является ответом, который кэшируется для последующих запросов.

// CachedController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace HttpCacheTest.Controllers
{
    public class CachedController : Controller
    {
        //
        // GET: /Cached/

        [OutputCache(Duration=20, VaryByParam="*")]
        public ActionResult Index()
        {
            var start = DateTime.Now;

            var i = Int32.MaxValue;
            while (i > 0)
            {
                i--;
            }
            var end = DateTime.Now;

            return Content( end.Subtract(start).ToString() );
        }

    }
}
0 голосов
/ 21 января 2016

Вы должны проверить эту информацию здесь : «У вас есть один клиент, делающий несколько одновременных запросов к серверу. Поведение по умолчанию заключается в том, что эти запросы будут сериализованы;»

Таким образом, если параллельный запрос от одного клиента сериализован, последующий запрос будет использовать кэш. Это объясняет некоторое поведение, которое можно найти в ответе выше (@ mats-nilsson и @ nick-craver)

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

...