Poprawa wydajności dzięki buforowaniu danych wyjściowych (C#)

autor: Microsoft

Z tego samouczka dowiesz się, jak znacznie poprawić wydajność aplikacji internetowych platformy ASP.NET MVC, korzystając z buforowania danych wyjściowych. Dowiesz się, jak buforować wynik zwrócony z akcji kontrolera, aby ta sama zawartość nie musiała być tworzona za każdym razem, gdy nowy użytkownik wywołuje akcję.

Celem tego samouczka jest wyjaśnienie, w jaki sposób można znacznie poprawić wydajność aplikacji ASP.NET MVC, korzystając z pamięci podręcznej danych wyjściowych. Pamięć podręczna danych wyjściowych umożliwia buforowanie zawartości zwracanej przez akcję kontrolera. W ten sposób ta sama zawartość nie musi być generowana za każdym razem, gdy jest wywoływana ta sama akcja kontrolera.

Załóżmy na przykład, że aplikacja ASP.NET MVC wyświetla listę rekordów bazy danych w widoku o nazwie Index. Zwykle za każdym razem, gdy użytkownik wywołuje akcję kontrolera zwracającą widok Indeks, zestaw rekordów bazy danych musi zostać pobrany z bazy danych przez wykonanie zapytania bazy danych.

Jeśli z drugiej strony korzystasz z wyjściowej pamięci podręcznej, możesz uniknąć wykonywania zapytania bazy danych za każdym razem, gdy dowolny użytkownik wywołuje tę samą akcję kontrolera. Widok można pobrać z pamięci podręcznej zamiast ponownie wygenerować z akcji kontrolera. Buforowanie umożliwia uniknięcie nadmiarowej pracy na serwerze.

Włączanie buforowania danych wyjściowych

Buforowanie danych wyjściowych można włączyć, dodając atrybut [OutputCache] do akcji pojedynczego kontrolera lub całej klasy kontrolera. Na przykład kontroler na liście 1 uwidacznia akcję o nazwie Index(). Dane wyjściowe akcji Index() są buforowane przez 10 sekund.

Lista 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();
        }
    }
}

W wersjach beta ASP.NET MVC buforowanie danych wyjściowych nie działa dla adresu URL, takiego jak http://www.MySite.com/. Zamiast tego musisz wprowadzić adres URL, taki jak http://www.MySite.com/Home/Index.

Na liście 1 dane wyjściowe akcji Index() są buforowane przez 10 sekund. Jeśli wolisz, możesz określić znacznie dłuższy czas trwania pamięci podręcznej. Jeśli na przykład chcesz buforować dane wyjściowe akcji kontrolera przez jeden dzień, możesz określić czas trwania pamięci podręcznej 86400 sekund (60 sekund * 60 minut * 24 godziny).

Nie ma gwarancji, że zawartość będzie buforowana przez określony czas. Gdy zasoby pamięci stają się niskie, pamięć podręczna automatycznie eksmituje zawartość.

Kontroler macierzystki na liście 1 zwraca widok Indeks w liście 2. Nie ma nic specjalnego w tym widoku. Widok Indeks po prostu wyświetla bieżący czas (zobacz Rysunek 1).

Lista 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>

Rysunek 1. Buforowany widok indeksu

clip_image002

Jeśli wywołasz akcję Index() wiele razy, wprowadzając adres URL /Home/Index na pasku adresu przeglądarki i naciskając przycisk Odśwież/Załaduj ponownie w przeglądarce wielokrotnie, czas wyświetlany przez widok Indeks nie zmieni się przez 10 sekund. Ten sam czas jest wyświetlany, ponieważ widok jest buforowany.

Ważne jest, aby zrozumieć, że ten sam widok jest buforowany dla wszystkich użytkowników odwiedzających aplikację. Każda osoba, która wywołuje akcję Index(), otrzyma tę samą buforowana wersję widoku Indeks. Oznacza to, że ilość pracy, którą serwer internetowy musi wykonać w celu obsługi widoku indeksu, jest znacznie zmniejszona.

Widok na liście 2 robi coś naprawdę prostego. Widok wyświetla tylko bieżący czas. Można jednak równie łatwo buforować widok, który wyświetla zestaw rekordów bazy danych. W takim przypadku zestaw rekordów bazy danych nie musi być pobierany z bazy danych za każdym razem, gdy jest wywoływana akcja kontrolera zwracająca widok. Buforowanie może zmniejszyć ilość pracy, którą musi wykonać zarówno serwer internetowy, jak i serwer bazy danych.

Nie używaj dyrektywy %@ OutputCache %> strony <w widoku MVC. Dyrektywa ta krwawi z Web Forms świata i nie powinna być stosowana w aplikacji MVC ASP.NET.

Gdzie zawartość jest buforowana

Domyślnie w przypadku używania atrybutu [OutputCache] zawartość jest buforowana w trzech lokalizacjach: serwerze internetowym, serwerach proxy i przeglądarce internetowej. Możesz kontrolować dokładnie miejsce buforowania zawartości, modyfikując właściwość Location atrybutu [OutputCache].

Właściwość Location można ustawić na dowolną z następujących wartości:

· Wszelki

· Klienta

· Dalszych

· Serwera

· Brak

· ServerAndClient

Domyślnie właściwość Location ma wartość Dowolne. Istnieją jednak sytuacje, w których można buforować tylko w przeglądarce lub tylko na serwerze. Jeśli na przykład buforujesz informacje, które są spersonalizowane dla każdego użytkownika, nie należy buforować informacji na serwerze. Jeśli wyświetlasz różne informacje dla różnych użytkowników, należy buforować informacje tylko na kliencie.

Na przykład kontroler na liście 3 uwidacznia akcję o nazwie GetName(), która zwraca bieżącą nazwę użytkownika. Jeśli Jack zaloguje się do witryny internetowej i wywoła akcję GetName(), akcja zwróci ciąg "Hi Jack". Jeśli następnie Jill loguje się do witryny internetowej i wywołuje akcję GetName(), wówczas otrzyma również ciąg "Hi Jack". Ciąg jest buforowany na serwerze sieci Web dla wszystkich użytkowników, gdy Jack początkowo wywołuje akcję kontrolera.

Lista 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;
        }
    }
}

Najprawdopodobniej kontroler na liście 3 nie działa tak, jak chcesz. Nie chcesz wyświetlać komunikatu "Hi Jack" do Jill.

Nigdy nie należy buforować spersonalizowanej zawartości w pamięci podręcznej serwera. Jednak możesz chcieć buforować spersonalizowaną zawartość w pamięci podręcznej przeglądarki, aby zwiększyć wydajność. Jeśli buforujesz zawartość w przeglądarce, a użytkownik wywołuje tę samą akcję kontrolera wiele razy, zawartość można pobrać z pamięci podręcznej przeglądarki zamiast serwera.

Zmodyfikowany kontroler na liście 4 buforuje dane wyjściowe akcji GetName(). Jednak zawartość jest buforowana tylko w przeglądarce, a nie na serwerze. W ten sposób, gdy wielu użytkowników wywołuje metodę GetName(), każda osoba otrzymuje własną nazwę użytkownika, a nie nazwę użytkownika innej osoby.

Lista 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;
        }
    }
}

Zwróć uwagę, że atrybut [OutputCache] w liście 4 zawiera właściwość Location ustawioną na wartość OutputCacheLocation.Client. Atrybut [OutputCache] zawiera również właściwość NoStore. Właściwość NoStore służy do informowania serwerów proxy i przeglądarki o tym, że nie powinny przechowywać trwałej kopii buforowanej zawartości.

Różnicowanie pamięci podręcznej danych wyjściowych

W niektórych sytuacjach może być potrzebne różne buforowane wersje tej samej zawartości. Załóżmy na przykład, że tworzysz stronę wzorcową/szczegółową. Na stronie wzorcowej jest wyświetlana lista tytułów filmów. Po kliknięciu tytułu uzyskasz szczegóły wybranego filmu.

Jeśli buforujesz stronę szczegółów, szczegóły tego samego filmu będą wyświetlane niezależnie od tego, który film klikniesz. Pierwszy film wybrany przez pierwszego użytkownika będzie wyświetlany dla wszystkich przyszłych użytkowników.

Ten problem można rozwiązać, korzystając z właściwości VaryByParam atrybutu [OutputCache]. Ta właściwość umożliwia tworzenie różnych buforowanych wersji tej samej zawartości, gdy parametr formularza lub parametr ciągu zapytania różni się.

Na przykład kontroler na liście 5 uwidacznia dwie akcje o nazwach Master() i Details(). Akcja Master() zwraca listę tytułów filmów, a akcja Details() zwraca szczegóły wybranego filmu.

Lista 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();
        }


    }
}

Akcja Master() zawiera właściwość VaryByParam z wartością "none". Po wywołaniu akcji Master() zwracana jest ta sama buforowana wersja widoku głównego. Wszystkie parametry formularza lub parametry ciągu zapytania są ignorowane (zobacz Rysunek 2).

Rysunek 2. Widok /Movies/Master

clip_image004

Rysunek 3 . Widok /Movies/Details

clip_image006

Akcja Details() zawiera właściwość VaryByParam z wartością "Id". Gdy do akcji kontrolera są przekazywane różne wartości parametru Id, generowane są różne buforowane wersje widoku Szczegóły.

Ważne jest, aby zrozumieć, że użycie właściwości VaryByParam powoduje więcej buforowania i nie mniej. Dla każdej innej wersji parametru Id jest tworzona inna buforowana wersja widoku Szczegóły.

Właściwość VaryByParam można ustawić na następujące wartości:

* = Utwórz inną wersję z pamięci podręcznej za każdym razem, gdy formularz lub parametr ciągu zapytania jest różny.

none = Nigdy nie twórz różnych buforowanych wersji

Lista średników parametrów = Utwórz różne buforowane wersje, gdy dowolny z parametrów formularza lub ciągu zapytania na liście jest różny

Tworzenie profilu pamięci podręcznej

Alternatywą dla konfigurowania właściwości wyjściowej pamięci podręcznej przez zmodyfikowanie właściwości atrybutu [OutputCache] można utworzyć profil pamięci podręcznej w pliku konfiguracji internetowej (web.config). Utworzenie profilu pamięci podręcznej w pliku konfiguracji sieci Web zapewnia kilka ważnych korzyści.

Najpierw konfigurując buforowanie danych wyjściowych w pliku konfiguracji internetowej, można kontrolować sposób buforowania zawartości w jednej centralnej lokalizacji przez akcje kontrolera. Można utworzyć jeden profil pamięci podręcznej i zastosować go do kilku kontrolerów lub akcji kontrolera.

Po drugie możesz zmodyfikować plik konfiguracji sieci Web bez ponownego komplikowania aplikacji. Jeśli musisz wyłączyć buforowanie dla aplikacji, która została już wdrożona w środowisku produkcyjnym, możesz po prostu zmodyfikować profile pamięci podręcznej zdefiniowane w pliku konfiguracji sieci Web. Wszelkie zmiany w pliku konfiguracji sieci Web zostaną wykryte automatycznie i zastosowane.

Na przykład <sekcja konfiguracji sieci Web buforowania> w artykule Lista 6 definiuje profil pamięci podręcznej o nazwie Cache1Hour. Sekcja <buforowania> musi znajdować się w <sekcji system.web> pliku konfiguracji sieci Web.

Lista 6 — sekcja buforowania dla web.config

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

Kontroler na liście 7 ilustruje, jak można zastosować profil Cache1Hour do akcji kontrolera za pomocą atrybutu [OutputCache].

Lista 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");
        }
    }
}

Jeśli wywołasz akcję Index() uwidocznionej przez kontroler na liście 7, zostanie zwrócony ten sam czas przez 1 godzinę.

Podsumowanie

Buforowanie danych wyjściowych zapewnia bardzo łatwą metodę znacznego zwiększenia wydajności aplikacji ASP.NET MVC. W tym samouczku przedstawiono sposób użycia atrybutu [OutputCache] do buforowania danych wyjściowych akcji kontrolera. Przedstawiono również sposób modyfikowania właściwości atrybutu [OutputCache], takich jak właściwości Duration i VaryByParam, aby zmodyfikować sposób buforowania zawartości. Na koniec przedstawiono sposób definiowania profilów pamięci podręcznej w pliku konfiguracji sieci Web.