16 мая 2011 г.

Кэширование в ASP.NET Часть 1. Введение

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

В этой статье мы рассмотрим возможности кэширования данных, предоставляемые механизмом ASP.NET.
Стоит отметить, что разработчики ASP.NET прекрасно понимают важность возможности кэширования, поэтому ASP.NET предоставляет прекрасно организованный, гибкий и расширяемый механизм кэширования данных. Однако ничего совершенного не существует, поэтому и у этого механизма есть свои недостатки. Одни из ключевых – это то, что кэширование является локальным и ограничивается одним AppDomain. Такой механизм абсолютно не подходит для использования в сценариях веб-фермы или веб-сада.
Примечание. Одним из наиболее распространённых и эффективных решений для кэширования данных, который применяется в сценариях веб-фермы и веб-сада является с++ решение MemCache. Также набирает популярности механизм кэширования данных в NoSQL хранилищах(таких как MongoDB), однако такие решения, впрочем как и сами NoSQL хранилища, являются достаточно “сырыми”.
Тем не менее механизм кэширования ASP.NET отлично работает для локального кэширования и сегодня мы рассмотрим насколько просто управлять этим механизмом при разработке собственных сайтов. В данной серии статей мы рассмотрим следующие аспекты, связанный с использованием кэширования в ASP.NET:
  1. Класс Cache – архитектура и свойства;
  2. Работа с объектом Cache;
  3. Настройка зависимостей в объекте Cache;
  4. Зависимости от данных XML;
  5. Зависимости от базы данных SQL Server.
Класс Cache Данный класс представляет собой механизм управления кэшированием в ASP.NET, он находится в пространстве имен System.Web.Caching и существует в пределах одного приложения(одного AppDomain). Собственно он и является хранилищем данных(коллекцией), которые мы помещаем в кэш. Текущий объект класса Cache можно получить обратившись к свойству Cache страницы(класса Page) или объекта HttpContext. Класс Cache наследует тип Object и реализует интерфейс IEnumerable. Он построен на основе словаря. Какой класс будет использоваться для кэширования среде ASP.NET зависит от выбранного разработчиком сценария. Если процесс приложения работает с одним ядром процессора – будет использоваться класс CacheSingle, в случае многоядерного процессора – будет использоваться класс CacheMultiple. И в том и в другом случае все объекты будут хранится в смешанной таблице, доступ к которой будет обеспечен из любой точки приложения. Ядро встроенного кэша ASP.NET также используется средствами механизмов самого ASP.NET для хранения собственных системных объектов. Такие объекты находятся в private секции ядра кэша и разработчики не могут получить к ним непосредственный доступ. Класс Cache является защищенным, потокобезопасным объектом. Все необходимые процессы синхронизации обеспечивают внутренние механизмы данного класса, поэтому при работе с ним нет необходимости явно использовать блокировки. Также класс предоставляет удобный рабочий интерфейс, основанный на работе со словарем. Данные в кэше хранятся не все время работы приложения – объект Cache предоставляет возможность установить для каждого объекта абсолютное и относительное время хранения в кэше, а также приоритет объектов. Время хранения задается в секундах – по его истечению объект удаляется из кэша. Приоритет позволяет механизму кэширования определить какие данные можно автоматически удалить из кэша, при нехватке оперативной памяти. Класс Cache имеет следующие публичные свойства:
  1. Count – определяет количество объектов, которые в данный момент находятся кэше;
  2. Item – свойство-индексатор, которое предоставляет доступ к объекту кэша по ключу, доступно для чтения и записи;
  3. EffectivePercentagePhysicalMemoryLimit – доступно только для чтения, возвращает процент физической памяти, который может использоваться кэш-хранилищем;
  4. EffectivePrivateBytesLimit – доступно только для чтения, определяет количество байт, которые может использовать кэш-хранилище.
А также две публичные константы, которое могут использоваться для задания срока хранения при помещении объекта в кэш:
  1. NoAbsoluteExpiration – указывает, что данный объект не будет иметь абсолютного срока хранения(данная константа имеет тип DateTime и установлена в значение DateTime.MaxValue);
  2. NoSlidingExpiration – указывает, что для данной записи не применяется политика относительного кэширования(имеет тип TimeSpan со значением TimeSpan.Zero).
При помощи свойства Item можно помещать объекты в кэш и извлекать их из него(это самый простой способ), например следующим образом:
Cache[“my_key”] = value;
Данные хранятся в кэше под типом object, то есть вы можете положить в кэш данные любого типа. Ключом обычно является строка(с учетом регистра). Если данные с таким ключом уже существуют в коллекции кэша – они перезаписываются, если нет – создается новая запись. При занесении объекта в кэш при помощи индексатора объекта Cache(как в примере выше) – создается запись в кэше с рядом параметров по умолчанию, в частности этому объекту будет присвоен средний приоритет, к нему не будет применяться политика определенного времени хранения и при его удалении не будут выполнятся дополнительные действия. Таким образом данный объект будет хранится в кэше неопределенное количество времени, пока он не будет удален программно из кода или пока приложение не прекратит свою работу. Чтобы задать дополнительные параметры хранения для объекта в кэше следует использовать метод Insert класса Cache. Класс Cache имеет следующие методы:
  1. object Add(string key, object value, CacheDependency dependencies, DateTime absoluteExpiration, TimeSpan slidingExpiration, CacheItemPriority  priority, CacheItemRemovedCallback onRemoveCallback);
    Add – добавляет объект в кэш. При этом разработчик может указать приоритет объекта, срок хранения объекта в кэше и метод, который будет вызываться при удалении данного объекта из кэша. Если запись с таким ключом уже существует, метод Add генерирует исключение. Данный метод возвращает объект типа object, представляющий новую запись в кэше;

  2. object Get(string key);
    
    Get – возвращает запись из кэша, хранящуюся с указанным ключом. Если такая запись не найдена метод возвращает null;

  3. IDictionaryEnumerator GetEnumerator();
    
    GetEnumerator – возвращает объект-перечислитель, позволяющий перемещаться по всем элементам коллекции;

  4. void Insert(string key, object value, CacheDependency dependencies, DateTime absoluteExpiration, TimeSpan slidingExpiration, CacheItemPriority priority, CacheItemRemovedCallback onRemoveCallback);
    Insert – вставляет объект в кэш с заданным ключом, при этом разработчик может указать приоритет объекта, срок хранения объекта в кэше и метод, который будет вызываться при удалении данного объекта из кэша. Метод имеет 5 перегрузок(выше приведен метод с наибольшим количеством параметров) и тип void. Если объект с таким ключом уже содержится в кэше – он будет перезаписан;

  5. object Remove(string key);
    Remove – метод удаляем запись из кэша по указанному ключу. Он возвращает объект, который был удален из коллекции кэша, а если запись с таким ключом отсутствует – возвращает null.

Помещение объекта в кэш Теперь, когда мы познакомились с классом Cache, его методами и свойствами, давайте посмотрим каким образом собственно может проходить процесс внесения объектов в кэш и извлечения их из него. Мы, конечно будет использовать самый продвинутый метод для внесения объектов в кэш – Insert. Как я упоминал выше, данный метод имеет 5 перегрузок:
1) public void Insert(string key, object value);
   2) public void Insert(string key, object value, CacheDependency dependencies);
   3) public void Insert(string key, object value, CacheDependency dependencies, DateTime absoluteExpiration, TimeSpan slidingExpiration);
   4) public void Insert(string key, object value, CacheDependency dependencies, DateTime absoluteExpiration, TimeSpan slidingExpiration, CacheItemUpdateCallback onUpdateCallback);
   5) public void Insert(string key, object value, CacheDependency dependencies, DateTime absoluteExpiration, TimeSpan slidingExpiration, CacheItemPriority priority, CacheItemRemovedCallback onRemoveCallback);     

Давайте рассмотрим параметры этого метода подробнее:
  1. Ключ(key) – строковое значение(с учетом регистра), являющееся идентификатором записи в коллекции кэша. Значение не должно быть null. Если запись с таким ключом уже существует в коллекции, при использовании метода Insert, значение объекта будет обновлено;

  2. Значение(value) – объект типа object. который необходимо закэшировать. Не может быть null;

  3. Зависимости(dependencies) – объекты типа CacheDependency. которые определяют зависимость объекта, хранящегося в кэше, от определенных объектов(это могут быть файлы, таблицы в базе данных и другие объекты), при изменении последних, запись в кэше помечается как устаревшая и автоматически удаляется;

  4. Абсолютное время хранения объекта в кэше(absoluteExpiration) – объект типа DateTime, который определяет статическое время, по истечении которого данных объект будет автоматически удален из кэша. При обращении к объекту время не обновляется. Чтобы отключить это режим хранения необходимо задать этот параметр как DateTime.MaxValue или воспользоваться константой NoAbsoluteExpiration;

  5. Относительное время хранения объекта в кэше(slidingExpiration) – объект типа TimeSpan. Время хранения в таком случае устанавливается как текущее плюс переданное значение. При этом при каждом обращении к этой записи время хранения будет автоматически переустанавливаться. Такой механизм, на мой взгляд, позволяет всегда держать в кэше наиболее востребованные данные.

  6. Приоритет(priority) – константа из перечисления CacheItemPriority, которое устанавливает значимость данного элемента в кэше. Данное значение учитывается механизмом кэширования при автоматическом удалении записей из кэша, при нехватке физической памяти. Записи с низким приоритетом удаляются в первую очередь;

  7. Функция обратного вызова(onRemoveCallback) – указывается название функции, которая будет вызвана при удалении данного из кэша(программно или автоматически).
Рассмотри следующий пример:
public class MyClass { }
Cache.Insert("key_1", new MyClass(), null, DateTime.Now.AddMinutes(10), Cache.NoSlidingExpiration, CacheItemPriority.Normal, null);
В данном примере в кэш помещается объект типа MyClass с ключом “key_1” и стандартным приоритетом, этот объект будет удален из кэша через 10 минут. Удаление объекта из кэша Объекты которые имеют зависимости или политику контроля времени хранения удаляются автоматически, при наступлении соответствующего события. Однако, также, программный интерфейс позволяет напрямую удалить объект из кэша. Операция производится следующим способом:
object Value = Cache.Remove(“my_key”);
В этом примере метод Remove удалит из кэша запись с ключом my_key, какой бы у нее не был приоритет и вернет удаленный из кэша объект. Если записи с таким ключом нет, метод вернет null. Если удаляемый объект содержит функцию обратного вызова, то при удалении она будет вызова и в нее будет передан ключ, значение удаляемого объекта и причина удаления. Причина удаления будет представлена перечислением CacheItemRemovedReason. Это перечисление содержит следующие значения:
  1. DependencyChanged – указывает, что объект удаляется из-за изменения объекта зависимости;

  2. Expired – объект удаляется из-за истечения абсолютного или относительного срока хранения;

  3. Removed – передается, при явном удалении посредством метода Cache.Remove. Также данная константа может передаваться в случае перезаписи объекта с использование метода Cache.Insert;

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

Комментариев нет:

Отправить комментарий