ГГУ им. Ф. Скорины

http://www.gsu.by

Разработка Web-приложений ASP .NET с использованием
Visual Studio .NET

9. Лекция: Использование кэширования в
Web-приложениях: версия для печати и PDA
Рассматриваются вопросы использования различных видов
кэширования, поддерживаемых ASP.NET. Приводятся примеры применения
кэширования на страницах ASP.NET приложения.

Цель лекции: изучить принципы использования
кэширования при создании Web-приложений. На практических примерах
рассмотреть возможности ASP.NET по организации различных видов
кэширования как целых страниц, так и их частей.

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

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

Но применение кэширования не только приводит к повышению
производительности и масштабируемости, но и создает ряд проблем,
которые необходимо знать и уметь их обходить. Одна из таких проблем
- это то, что кэширование задействует
оперативную память, которая никогда не бывает лишней. Если
попытаться сохранить в оперативной памяти слишком много данных,
операционная система сбрасывает лишние данные на диск, что может
привести к замедлению работы всей системы в целом. Для управления
этим процессом в ASP.NET реализован интеллектуальный механизм
определения переполнения кэша, который основан на том, что при
попытке записи в кэш данных, объем которых превышает доступный для
кэша объем оперативной памяти, ASP.NET выборочно удалит из кэша
часть данных для обеспечения максимальной общей производительности
системы. Кроме того, при использовании механизмов кэширования
необходимо помнить, что должен быть некий принцип, согласно которому
будет осуществляться проверка обновления информации в кэше. Если
данные таблицы базы данных или файла изменились, это может означать,
что данные кэша устарели и их необходимо обновить. ASP.NET 2.0
предоставляет усовершенствованный механизм управления кэшированием, реализуя все необходимые элементы
управления политикой кэширования. В частности, он позволяет
управлять замещением информации, находящейся в кэше, управлять
профилями кэшей, с помощью которых можно определить настройки
кэширования для группы страниц, управлять хранением содержимого кэша
в оперативной памяти и на жестких дисках, отслеживать изменения в
исходных данных и удалять или объявлять недействительными
кэшированные элементы.

Основы кэширования в ASP.NET

ASP.NET поддерживает два типа кэширования: кэширование данных и кэширование вывода. Рекомендуется использовать
оба эти типа кэширования, т. к. это способно значительно повысить
производительность приложения.

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

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

Упомянутые виды кэширования стали основой для создания
еще двух разновидностей.

Кэширование фрагментов - позволяет
сохранять в кэше лишь часть готового HTML-кода страницы. В
основном это относится к пользовательским элементам управления.

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

Рассмотрим основные аспекты применения кэширования в
ASP.NET

Кэширование вывода

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

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

Создадим новое Web-приложение, откроем редактор кода
страницы и введем следующую команду в обработчик события Page_Load:

Response.Write(DateTime.Now.ToString());

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

Рис. 9.1.  Результат
отображения текущей даты и времени в окне браузера без использования
кэширования

Добавим данную страницу в кэш. Для этого добавим к файлу
страницы директиву

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

Атрибут Duration задает
количество секунд, в течение которых необходимо хранить страницу в
кэше. Параметр VaryByParam, равный
значению None, устанавливает режим
кэширования, при котором, независимо от дополнительных параметров, в
кэше будет сохраняться только одна копия данной страницы.

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

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

Выше уже отмечались некоторые потенциальные проблемы,
связанные с кэшированием страницы вывода. Одним из таких случаев
является использование данных, передаваемых странице через строку
запроса. При установленном режиме кэширования, рассматриваемом выше,
несмотря на ввод в строку адреса значений переменных страница
сохраняется в кэше заданные 10 секунд. Однако такое поведение можно
изменить, если указать в качестве значения параметра VaryByParam значение "*", которое
устанавливает режим кэширования страниц, содержащих строку запроса.
При этом ASP.NET начинает кэшировать отдельные копии страницы для
разных значений аргументов, указанных в строке запроса.

Рис. 9.2.  Кэширование
страницы для разных значений аргументов, указанных в строке запроса

Использование значения "*" в качестве значения параметра
VaryByParam в некоторых случаях способно
вызвать дополнительные проблемы. Дело в том, что в некоторых типах
приложений достаточно часто применяется передача множества
параметров в строке запроса. Если значение хотя бы одного из
передаваемых параметров будет отличаться от значения такого же
параметра кэшированной страницы, запрашиваемая страница будет
кэширована вновь. Например, если в строке запроса передается
информация о клиенте, а также о выбранном им товаре, понятно, что
количество комбинаций клиента и товара достаточно велико, а
следовательно, практически все запрашиваемые страницы будут помещены
в кэш, причем их повторное использование будет стремиться к 0. В
этом случае целесообразно выявить те параметры, которые наиболее
часто нужны для передачи параметров с высокой степенью повторного
использования, и указать их в качестве значения параметра VaryByParam. Например, следующая строка дает
команду ASP.NET для кэширования только тех страниц, которые содержат
параметр ClientID, чье значение, в свою
очередь, отличается от уже сохраненного в кэше.

<%@ OutputCache Duration="30" VaryByParam="ClientName" %>

Фрагментное кэширование

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

Реализовать данную разновидность кэширования можно двумя
способами.

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

Создать свой метод, который должен возвращать некоторое
значение. С помощью объекта Substitution указать на необходимость вызова
данного метода при обращении пользователя к Web-странице. При этом
сама страница может быть кэширована, однако данный метод будет
выполняться в любом случае, возвращая фактически динамическое
содержимое, которое будет вставлено в кэшированный фрагмент
страницы; причем такой сценарий обработки будет применяться
всегда. Фактически, получается реализация, обратная описанной в
первом пункте.

Рассмотрим примеры применения фрагментного кэширования, соответствующего
обоим описанным способам.

Использование пользовательского элемента управления для
реализации фрагментного кэширования

Для реализации данного вида кэширования создадим
пользовательский элемент управления, отображающий текущее время, а
также цветную полосу, величина которой изменяется в соответствии со
значением количества секунд текущего времени [4].

Для создания пользовательского элемента управления
необходимо выполнить команду меню WebSite р Add New Item и в
открывшемся окне выбрать пункт Web User
Control.

Рис. 9.3.  Создание и
добавление к проекту пользовательского элемента управления

В результате будет создан и добавлен к приложению файл
ShowTime.ascx, представляющий собой
фактически Web-страницу, у которой отсутствуют обязательные для
HTML-страниц теги, такие как <html>, <body> и т. д. Отсутствие упомянутых
тегов объясняется тем, что пользовательский элемент управления
фактически представляет собой фрагмент Web-страницы, который будет
встраиваться в основную страницу.

Введем в исходный код созданного пользовательского
элемента управления следующий фрагмент:

<div class="box">
<div runat="server" id="timeLabel" class="bar"> <asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>
</div>
</div>

Фактически этот пользовательский элемент управления
создается на основе серверного элемента управления Label, который помещен внутри двух тэгов <div>. Тег верхнего уровня необходим для
формирования контура элемента управления в виде прямоугольника.
Второй тег <div> нужен для
отображения цветной полосы, величина которой зависит от текущего
значения секунд. Если с контуром в виде прямоугольника все просто,
то о цветной полосе стоит поговорить отдельно. В принципе, она
должна являться аналогом элемента progress
bar в традиционных win32-приложениях. Реализовать в рамках
Web-приложения это можно несколькими способами, один из которых
подразумевает отображение прямоугольника с необходимым цветом фона
внутри элемента управления Label и
изменение его размера в зависимости от значения времени. Сделать это
можно с помощью CSS, создав несколько описаний стилей классов,
обозначенных в исходном коде пользовательского элемента управления,
который приведен выше.

Пример описания стилей, использованных для данного
примера, приведен ниже.

body
{
font-family:Lucida Sans;
font-size:1em;
}
.box
{
position: relative;
width: 120px;
border: 1px solid gray;
padding: 2px;
}
.box .bar
{
background: lightblue;
height: 1em;
line-height: 1em;
}
.box .bar span
{
position: absolute;
font-size:small;
}

Теперь необходимо позаботиться о том, чтобы внутри
созданного пользовательского элемента управления отображалось
текущее значение времени. Для этого необходимо в обработчик события
Page_Load пользовательского элемента
ввести следующий код:

protected void Page_Load(object sender, EventArgs e)
{
Label1.Text = DateTime.Now.ToString("hh:mm:ss");
timeLabel.Style.Add("width", (DateTime.Now.Second * 2).
ToString() + "px");
}

Из примера видно, что значению элемента Label1 присваивается значение текущего времени
в формате "час:минута:секунда". После этого к элементу timeLabel добавляется стиль, определяющий
значение ширины элемента и в качестве значения ширины
устанавливающий удвоенное значение текущего количества секунд. Здесь
следует обратить внимание на то, что элемент timeLabel на самом деле является
HTML-элементом <div>, в параметрах
которого установлено значение runat="server", что делает возможным обращение
к его параметрам на стороне сервера.

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

Рис. 9.4.  Отображение
пользовательского элемента на странице без использования кэширования

Создадим копию пользовательского элемента управления
ShowTime.ascx и назовем его ShowTime_cache.ascx. Установим для него режим
кэширования. Для этого, так же как и в обычной HTML-странице, введем
директиву

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

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

Рис. 9.5.  Размещение
пользовательских элементов управления без поддержки и с поддержкой
кэширования

В результате после запуска приложения получим следующий
результат (рис.
9.6).

Рис. 9.6.  Результат
работы программы с использованием кэширующего и не кэширующего
элементов управления

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

[PartialCaching(20)]
public partial class ShowTime : System.Web.UI.UserControl
{
}

Рис. 9.7.  Добавление
пользовательского элемента управления, параметры кэширования
которого задаются с помощью свойств класса

Использование элемента управления Substitution для реализации
фрагментного кэширования

Рассматривая примеры, приведенные выше, нетрудно
заметить, что параметры кэширования для страницы, на которую
помещаются элементы управления, не устанавливались. Это приводило к
тому, что кэшировались только те элементы, для которых были
установлены соответствующие параметры. Иногда бывает полезнее
поступить наоборот: установить параметры кэширования для всей
страницы, а для содержимого некоторых элементов сделать возможным
отсутствие кэширования. В этом случае нужно воспользоваться
элементом Substitution. Для работы этому
элементу управления необходимо название статического метода, который
возвращает динамическое содержимое, отображаемое на странице.
Элемент Substitution может быть размещен
на странице так же, как и любые другие элементы управления, что
позволяет контролировать расположение его содержимого внутри
страницы.

В качестве примера создадим новую Web-страницу.
Установим для нее следующий параметр кэширования:

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

Поместим на вновь созданную страницу элемент ShowTime.ascx, не осуществляющий кэширование,
а также элемент Substitution, в качестве
значения параметра MethodName которого
установим GetTime. Это означает, что
элемент Substitution при обращении к
странице будет инициировать вызов процедуры GetTime. Эта процедура должна быть статической
процедурой в составе класса страницы, возвращающей результат в виде
строки. Именно возвращенное значение и будет подставлено в место
расположения элемента Substitution. Код
метода GetTime при этом может выглядеть
следующим образом:

public static string GetTime(HttpContext context)
{
return "Текущее время:" + DateTime.Now.ToString("hh:mm:ss");
}

Результат выполнения данной страницы представлен на рис.
9.8. Как видно, содержимое страницы кэшируется, однако элемент
Substitution выводит на экран текущее
значение времени, параметры кэширования к которому не
применяются.

Рис. 9.8.  Использование
фрагментного кэширования при помощи элемента Substitution

Рассмотренные способы предоставляют возможность
использовать встроенные в ASP.NET возможности кэширования. Тем не
менее существует возможность использования собственного алгоритма
кэширования данных, при котором в программном коде определяются
принципы помещения и извлечения данных из кэша.

Краткие итоги

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

ASP.NET поддерживает два типа кэширования: кэширование данных и кэширование вывода.

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

© gsu.by