Поделиться через


Повышение производительности благодаря кэшированию вывода (C#)

от Корпорации Майкрософт

Из этого руководства вы узнаете, как значительно повысить производительность веб-приложений ASP.NET MVC, используя кэширование выходных данных. Вы узнаете, как кэшировать результат, возвращаемый действием контроллера, чтобы не создавать одно и то же содержимое каждый раз, когда новый пользователь вызывает действие.

Цель этого руководства — объяснить, как можно значительно повысить производительность приложения ASP.NET MVC, используя преимущества кэша вывода. Кэш выходных данных позволяет кэшировать содержимое, возвращаемое действием контроллера. Таким образом, не нужно создавать одно и то же содержимое при каждом вызове одного и того же действия контроллера.

Представьте, например, что приложение MVC ASP.NET отображает список записей базы данных в представлении Index. Как правило, каждый раз, когда пользователь вызывает действие контроллера, которое возвращает представление индекса, набор записей базы данных должен быть извлечен из базы данных путем выполнения запроса к базе данных.

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

Включение кэширования выходных данных

Кэширование выходных данных можно включить, добавив атрибут [OutputCache] к отдельному действию контроллера или ко всему классу контроллера. Например, контроллер в листинге 1 предоставляет действие с именем Index(). Выходные данные действия Index() кэшируются в течение 10 секунд.

Листинг 1. Controllers\HomeController.cs

using System.Web.Mvc;

namespace MvcApplication1.Controllers
{
    [HandleError]
    public class HomeController : Controller
    {
        [OutputCache(Duration=10, VaryByParam="none")]
        public ActionResult Index()
        {
            return View();
        }
    }
}

В бета-версиях ASP.NET MVC кэширование выходных данных не работает для URL-адреса, например http://www.MySite.com/. Вместо этого необходимо ввести URL-адрес, например http://www.MySite.com/Home/Index.

В листинге 1 выходные данные действия Index() кэшируются в течение 10 секунд. При желании можно указать гораздо больший срок действия кэша. Например, если требуется кэшировать выходные данные действия контроллера в течение одного дня, можно указать длительность кэша 86400 секунд (60 секунд * 60 минут * 24 часа).

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

Контроллер Home в листинге 1 возвращает представление индекса в листинге 2. В этом представлении нет ничего особенного. В представлении Индекс просто отображается текущее время (см. рис. 1).

Листинг 2. Views\Home\Index.aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Index.aspx.cs" Inherits="MvcApplication1.Views.Home.Index" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Index</title>
</head>
<body>
    <div>
    
    The current time is: <%= DateTime.Now.ToString("T") %>
    
    
    </div>
</body>
</html>

Рис. 1. Представление кэшированного индекса

clip_image002

Если вы вызываете действие Index() несколько раз, введя URL-адрес /Home/Index в адресной строке браузера и нажимая кнопку Обновить/перезагрузить в браузере, время, отображаемое представлением индекса, не изменится в течение 10 секунд. Отображается то же время, так как представление кэшируется.

Важно понимать, что одно и то же представление кэшируется для всех пользователей, посещающих ваше приложение. Любой пользователь, который вызывает действие Index(), получит такую же кэшированную версию представления индекса. Это означает, что объем работы, которую должен выполнить веб-сервер для обслуживания представления индекса, значительно сокращается.

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

Не используйте директиву %@ OutputCache %> страницы <в представлении MVC. Эта директива истекает из веб-формы мира и не должна использоваться в ASP.NET приложении MVC.

Место кэширования содержимого

По умолчанию при использовании атрибута [OutputCache] содержимое кэшируется в трех расположениях: веб-сервер, все прокси-серверы и веб-браузер. Вы можете управлять тем, где именно кэшируется содержимое, изменив свойство Location атрибута [OutputCache].

Для свойства Location можно задать любое из следующих значений:

· Любой

· Клиента

· Вниз по течению

· Сервера

· Ни один

· ServerAndClient

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

Например, контроллер в листинге 3 предоставляет действие с именем GetName(), которое возвращает текущее имя пользователя. Если Джек входит на веб-сайт и вызывает действие GetName(), то действие возвращает строку "Hi Jack". Если впоследствии Джилл войдет на веб-сайт и вызовет действие GetName(), она также получит строку "Hi Jack". Строка кэшируется на веб-сервере для всех пользователей после того, как Джек изначально вызывает действие контроллера.

Листинг 3. Controllers\BadUserController.cs

using System.Web.Mvc;
using System.Web.UI;

namespace MvcApplication1.Controllers
{
    public class BadUserController : Controller
    {
        [OutputCache(Duration = 3600, VaryByParam = "none")]
        public string GetName()
        {
            return "Hi " + User.Identity.Name;
        }
    }
}

Скорее всего, контроллер в листинге 3 работает не так, как вам нужно. Вы не хотите отображать сообщение "Привет, Джек" для Джилл.

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

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

Листинг 4. Controllers\UserController.cs

using System.Web.Mvc;
using System.Web.UI;

namespace MvcApplication1.Controllers
{
    public class UserController : Controller
    {
        [OutputCache(Duration=3600, VaryByParam="none", Location=OutputCacheLocation.Client, NoStore=true)]
        public string GetName()
        {
            return "Hi " + User.Identity.Name;
        }
    }
}

Обратите внимание, что атрибут [OutputCache] в листинге 4 содержит свойство Location, задающее значение OutputCacheLocation.Client. Атрибут [OutputCache] также содержит свойство NoStore. Свойство NoStore используется для информирования прокси-серверов и браузера о том, что они не должны хранить постоянную копию кэшированного содержимого.

Изменение кэша выходных данных

В некоторых ситуациях может потребоваться разные кэшированные версии одного и того же содержимого. Представьте, например, что вы создаете страницу master или сведений. На странице master отображается список названий фильмов. Щелкнув заголовок, вы получите сведения о выбранном фильме.

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

Эту проблему можно устранить, воспользовавшись свойством VaryByParam атрибута [OutputCache]. Это свойство позволяет создавать разные кэшированные версии одного и того же содержимого, если параметр формы или параметр строки запроса меняется.

Например, контроллер в листинге 5 предоставляет два действия с именами Master() и Details(). Действие Master() возвращает список названий фильмов, а действие Details() — сведения о выбранном фильме.

Листинг 5. Controllers\MoviesController.cs

using System.Linq;
using System.Web.Mvc;
using MvcApplication1.Models;

namespace MvcApplication1.Controllers
{
    public class MoviesController : Controller
    {
        private MovieDataContext _dataContext;

        public MoviesController()
        {
            _dataContext = new MovieDataContext();
        }

        [OutputCache(Duration=int.MaxValue, VaryByParam="none")]
        public ActionResult Master()
        {
            ViewData.Model = (from m in _dataContext.Movies 
                              select m).ToList();
            return View();
        }

        [OutputCache(Duration = int.MaxValue, VaryByParam = "id")]
        public ActionResult Details(int id)
        {
            ViewData.Model = _dataContext.Movies.SingleOrDefault(m => m.Id == id);
            return View();
        }


    }
}

Действие Master() содержит свойство VaryByParam со значением none. При вызове действия Master() возвращается та же кэшированная версия главного представления. Все параметры формы или параметры строки запроса игнорируются (см. рис. 2).

Рис. 2. Представление /Movies/Master

clip_image004

Рис. 3. Представление /Movies/Details

clip_image006

Действие Details() содержит свойство VaryByParam со значением "Id". При передаче различных значений параметра Id в действие контроллера создаются разные кэшированные версии представления Сведений.

Важно понимать, что использование свойства VaryByParam приводит к большему и не меньшему кэшированию. Для каждой версии параметра Id создается другая кэшированная версия представления Сведений.

Для свойства VaryByParam можно задать следующие значения:

* = создавайте другую кэшированную версию при каждом изменении параметра формы или строки запроса.

none = никогда не создавайте разные кэшированные версии

Список параметров с запятой = создание различных кэшированных версий при каждом изменении параметров формы или строки запроса в списке

Создание профиля кэша

В качестве альтернативы настройке свойств кэша вывода путем изменения свойств атрибута [OutputCache] можно создать профиль кэша в файле веб-конфигурации (web.config). Создание профиля кэша в файле веб-конфигурации дает ряд важных преимуществ.

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

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

Например, раздел <веб-конфигурации кэширования> в листинге 6 определяет профиль кэша с именем Cache1Hour. Раздел <кэширования> должен находиться в <разделе system.web> файла веб-конфигурации.

Листинг 6. Раздел кэширования для web.config

<caching>
<outputCacheSettings>
    <outputCacheProfiles>
        <add name="Cache1Hour" duration="3600" varyByParam="none"/>
    </outputCacheProfiles>
</outputCacheSettings>
</caching>

Контроллер в листинге 7 показывает, как можно применить профиль Cache1Hour к действию контроллера с атрибутом [OutputCache].

Листинг 7. Controllers\ProfileController.cs

using System;
using System.Web.Mvc;

namespace MvcApplication1.Controllers
{
    public class ProfileController : Controller
    {
        [OutputCache(CacheProfile="Cache1Hour")]
        public string Index()
        {
            return DateTime.Now.ToString("T");
        }
    }
}

Если вы вызываете действие Index(), предоставляемое контроллером в листинге 7, то же время будет возвращено в течение 1 часа.

Итоги

Кэширование выходных данных обеспечивает очень простой способ существенного повышения производительности ASP.NET приложений MVC. В этом руководстве вы узнали, как использовать атрибут [OutputCache] для кэширования выходных данных действий контроллера. Вы также узнали, как изменять свойства атрибута [OutputCache], например свойства Duration и VaryByParam, чтобы изменить способ кэширования содержимого. Наконец, вы узнали, как определять профили кэша в файле веб-конфигурации.