ASP.NET (MVC) Кэш вывода и параллельные запросы

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

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

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

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

Спасибо!


person Razzie    schedule 29.01.2010    source источник


Ответы (5)


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

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

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

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

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

person Craig Stuntz    schedule 29.01.2010
comment
Благодарю небеса, у меня действительно нет запроса, который занимает 10 секунд, но я сильно преувеличил, чтобы проиллюстрировать точку зрения. Мне просто было интересно, что произойдет в таком случае. Спасибо! Однако можно рассмотреть возможность реализации асинхронных контроллеров. - person Razzie; 29.01.2010
comment
было какое-то время ... только что сделал тест сам, и я уверен, что он не блокируется, как вы сказали. Спасибо. - person Razzie; 19.10.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);
}

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

person Nick Craver    schedule 29.01.2010
comment
Вы тестируете на WebDev? Он ведет себя очень иначе, чем IIS на многоядерном сервере. - person Craig Stuntz; 29.01.2010
comment
@Craig: тестирование с использованием IIS 7.5, Windows 7 x64 на четырехъядерном процессоре. - person Nick Craver; 29.01.2010
comment
Это кажется странным. Возможно, вы используете режим отладки? Это действительно не должно блокироваться. Другие запросы должны получить промах кеша. - person Craig Stuntz; 29.01.2010
comment
@Craig: Нет, режим выпуска ... просто подключение отладчика в IIS, но, конечно, возможно, это влияет на поведение. С другой стороны, это было бы оптимальным поведением для IIS, чтобы другие запросы ожидали и все получали результат первого обращения, которое обрабатывается. Если вы указываете кеш вывода, вы как бы говорите, что не хотите, чтобы эта вещь обрабатывалась часто, и IIS, действующий таким образом, выполнит это и по-прежнему будет обслуживать все запросы как можно быстрее (поток первого запроса должен заканчиваться первым). , в большинстве случаев). - person Nick Craver; 29.01.2010
comment
Если запрос все еще обрабатывается, совсем не очевидно, что следующий запрос попадет в кеш, даже если первый запрос завершится. Я бы предложил тестирование с ведением журнала, а не с отладчиком. - person Craig Stuntz; 29.01.2010
comment
@Craig: Можете ли вы объяснить немного больше, что вы имеете в виду? Если пользователь делает запрос № 2, который приведет к тому же результату (как и в совпадении VarByWhatever), не должен ли IIS дождаться завершения запроса и вернуть этот результат всем, кто попал бы в кеш, если бы они натолкнулись на то же самое? мгновения спустя? Должен быть такой же результат. (Если это не так, вам не следует использовать OutputCache). Если бы он этого не делал и вы получили 1000 обращений одновременно, вы бы обрабатывали один и тот же результат 1000 раз, а не один раз быстро и возвращались к 1000 запросам. .имеет смысл, что он будет вести себя таким образом. - person Nick Craver; 29.01.2010
comment
Кэш может иметь зависимость. Его также можно очистить другим кодом. - person Craig Stuntz; 29.01.2010
comment
@Craig, это справедливое замечание, но все они будут просить страницу в том виде, в каком она была в тот момент, когда они все пошли на это, так что все еще может быть. Я попробовал аналогичный тест, как вы просили здесь, режим выпуска, без отладки со статическим целым числом, которое увеличивается на каждый процесс страницы ... тот же результат, число увеличивается только один раз. Я обновлю ответ, чтобы показать этот подход. - person Nick Craver; 29.01.2010
comment
Я считаю, что что-то здесь не так, но у меня нет времени разбираться с этим самому. Так что +1 за тестирование, и я обновлю свой ответ, чтобы отразить то, что вы сделали. - person Craig Stuntz; 29.01.2010
comment
@Craig: я ценю интеллектуальную дискуссию. Пожалуйста, проведите тест... так как я буду использовать это в ближайшем будущем, мне было бы очень любопытно, если бы у вас было другое поведение. - person Nick Craver; 29.01.2010

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

Пример кода:

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.

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

person Mats Nilsson    schedule 11.12.2012

Я сделал небольшой тест, который может помочь. Я считаю, что я обнаружил, что некэшированные запросы не блокируются, и каждый запрос, который поступает, когда срок действия кеша истек и до завершения задачи, ТАКЖЕ запускает эту задачу.

Например, приведенный ниже код занимает около 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() );
        }

    }
}
person Andrew    schedule 24.05.2011

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

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

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

person mqueirozcorreia    schedule 21.01.2016