18 мая 2011 г.

Кэширование в ASP.NET Часть 2. Работа с объектом Cache

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


Задание приоритета для кэшируемого объекта
Напомню вам описание основного метода для помещения объекта в кэш:
void Insert(string key, object value, CacheDependency dependencies, DateTime absoluteExpiration, TimeSpan slidingExpiration, CacheItemPriority priority, CacheItemRemovedCallback onRemoveCallback);

Шестым параметром в этом методе является значение из перечисления CacheItemPriority. Это параметр имеет очень важное значение. Опираясь на него механизм кэширования ASP.NET определяет какие объекты можно в первую очередь удалять, в случае нехватки физической памяти машины. Данное поведение является одним из конкурентных преимуществ объекта Cache. Ведь именно разработка интеллектуальной системы управления рабочей памятью машины была одной из основных задач, которые стояли перед разработчиками механизм кэширования в ASP.NET.

Приоритет может быть установлен в следующие значения:

  1. Low – самый низкий приоритет, записи с таким приоритетом при освобождении памяти удаляются из кэша в первую очередь;

  2. BelowNormal – приоритет у объекта ниже среднего;
  3. Normal – среднее значение приоритета, автоматически устанавливается для всех объектов, добавляемых в кэш, при отсутствии явного указания приоритета;

  4. Default – значение по умолчанию, тоже самое что и Normal;
  5. AboveNormal – выше среднего;
  6. High – объект, помещаемый в кэш будет иметь высокий приоритет. При автоматическом освобождении системной памяти объекты с высоким приоритетом будут удаляться из кэша в последнюю очередь;

  7. NotRemovable – наивысший уровень приоритета. Объекты с таким приоритетом никогда не будут автоматически удалены из кэша. То есть время нахождения объекта в кэше контролирует только разработчик. В связи с этим разработчики должны соблюдать высочайшую осторожность при использовании данного приоритета, дабы предотвратить неконтролируемую утечку памяти.


Контроль срока хранения объекта в кэше
Конечно разработчики вправе самостоятельно контролировать время нахождения их объектов в кэше ASP.NET. Один из наиболее эффективных способов это сделать – применение временного интервала хранения объекта.
По умолчанию, объекты, помещаемые в кэш, не имеют фиксированного времени хранения. Однако разработчик может это изменить, установив время, о истечении которого механизм ASP.NET должен автоматически удалить объект из кеша. В этом случае разработчику предлагается на выбор две стратегии задания времени хранения:
  1. Абсолютное время хранения – в этом случае, при помещении объекта в кэш, разработчик передает в метод Insert или Add объект типа DateTime, который содержит определенную дату и время. При наступлении этой даты объект удаляется из кэша. Если разработчик не хочет устанавливать этот параметр – он может указать его как DateTime.MaxValue или передать константу Cache.NoAbsoluteExpiration. Тем самым устанавливается бесконечное время хранения объекта;

  2. Относительное время хранения – если в предыдущем режиме время удаления объекта было фиксированным, то в данном случае время жизни объекта в кэше может автоматически переустанавливаться. Переустановка происходит при каждом обращении какого-либо кода к данному объекту в кэше. Таким образом объект не будет удалятся из кэша, пока он востребован приложением разработчика. На мой взгляд, это один из наиболее эффективных сценариев использования кэша в ASP.NET.
Рассмотрим следующие примеры:
Cache.Insert("key_1", new MyClass(), null, DateTime.Now.AddMinutes(10),  Cache.NoSlidingExpiration, CacheItemPriority.Normal, null);
В данном случае в кэш помещается объект, который будет автоматически удален через 10 минут.
Cache.Insert("key_1", new MyClass(), null, Cache.NoAbsoluteExpiration, TimeSpan.FromMinutes(5), CacheItemPriority.Normal, null);
Во втором случае время хранения для объекта устанавливается как текущее плюс 5 минут. И при каждом обращении к объекту в кэше данное время будет устанавливаться заново.


Установка зависимостей для объекта в кэше
Еще один способ контроля времени хранения объекта в кэше – установка зависимостей. Для помещаемого в кэш объекта устанавливается связь с определенным источником. В случае каким-либо изменений в связанном источнике, данный объект в кэше признается устаревшим и автоматически удаляется. В качестве источника может выступать одиночные файлы и каталоги, массивы файлов и каталогов, другие элементы в кэше, а также таблицы базы данных или внешние события.
Все вышеперечисленные зависимости устанавливаются при помощи класса CacheDependency или ряда его специализированных потомков. Данный класс имеет длинный список конструкторов, которые выполняют следующие функции:
  1. public CacheDependency(string filename);
    Указывается путь к файлу или каталогу, в котором должны отслеживаться изменения;

  2. public CacheDependency(string[] filenames);
    Задается массив путей к файлам или каталогам, в которых следует отслеживать изменения;

  3. public CacheDependency(string filename, DateTime start);
    Устанавливается путь к файлу или папке и время, начиная с которого нужно отслеживать изменения;

  4. public CacheDependency(string[] filenames, DateTime start);
    Задает массив путей к файлам и каталогам, а также время, начиная с которого необходимо отслеживать в них изменения;

  5. public CacheDependency(string[] filenames, string[] cachekeys);
    Указывает массив путей к файлам или каталогам, а также массив ключей кэша. Объект будет из кэша будет удалятся при изменениях в файлах, по заданным путям, или при изменениях в объектах кэша, с указанными ключами;

  6. public CacheDependency(string[] filenames, string[] cachekeys, CacheDependency dependency);
    Устанавливает массив путей к файлам или каталогам, массив ключей в кэше и дополнительный объект CacheDependency;

  7. public CacheDependency(string[] filenames, string[] cachekeys, DateTime start);
    Задает массив путей к файлам или каталогам, массив ключей кэша и время, начиная с которого данные зависимости начинают отслеживаться;

  8. public CacheDependency(string[] filenames, string[] cachekeys, CacheDependency dependency, DateTime start);
    Указывает массив путей к файлам или каталогам, массив ключей кэша, дополнительный объект CacheDependency и время, начиная с которого все эти зависимости начинают отслеживаться.
Некоторые конструкторы имеют одним из параметров объект DateTime, который устанавливает время, начиная с которого механизм кэширования ASP.NET должен следить за изменениями в связанной сущности. По умолчанию, мониторинг связанной сущности начинается немедленно после помещения объекта в кэш. Также, в несколько конструкторов можно передать дополнительный объект типа CacheDependency, это используется в случае необходимости комбинирования нескольких зависимостей.
После установки определенной зависимости механизм кэширования ASP.NET начинает мониторить связанную сущность(или сущности), проверяя наличие каких-либо изменений. Как только обнаружено изменение все связанные с данной сущностью объекты в кэше удаляются. Также, после удаления всех объектов из кэша, ASP.NET перестает мониторить изменения в связанной сущности, поскольку в кэше уже нету объектов, которые с ней связаны.
Рассмотрим несколько примеров:
CacheDependency depend = new CacheDependency("myfile.txt");
Cache.Insert("key_1", new MyClass(), depend);
Здесь показан самый простой случай – создается зависимость от файла myfile.txt, и заносится объект в кеш с указанием данной зависимости. При изменении файла myfile.txt – объект будет автоматически удален из кэша.
Cache.Insert("key_2", new MyClass());

CacheDependency depend = new CacheDependency(new string[]  {"myfile.txt"}, new string[] {"key_2"}, DateTime.Now.AddMinutes(10));
Cache.Insert("key_1", new MyClass(), depend);
В этом случае сначала кладется объект в кэш с ключом key_2, а затем устанавливается зависимость от файла myfile.txt и объекта в кэше с ключом key_2. Основной объект(с ключом key_1) будет удален из кэша при изменении файла myfile.txt или при изменении(удалении или обновлении) объекта в кэше с ключом key_2. Механизм кэширования начнет следить за связанными объектами через десять минут после помещения в кэш объекта с ключом key_1.


Определение функции обратного вызова
Мы рассмотрели выше множество параметров, в соответствии с которыми производится удаление объектов из кэша ASP.NET. Однако существует еще одна крайне полезная особенность, предоставляемая объектом Cache. Разработчик может установить функцию, которая будет выполнятся при удалении объекта из кэша.
Определенная функция привязывается к определенному объекту и срабатывает, начиная асинхронное выполнение, при удалении это объекта из кэша каким-либо способом. Для обеспечения максимального быстродействия рекомендуется объявлять функцию обратного вызова как статическую функцию. В против случае механизм ASP.NET будет вынужден поддерживать в памяти объект класса, содержащего данную функцию.
Рассмотрим пример занесения объекта в кэш с указанием функции обратного вызова:
    public class CacheDispatcher
    {
        public static List<string> Log = new List<string>();

        public static void NotifyFunction(string key, object value, CacheItemRemovedReason reason)
        {
            if (reason == CacheItemRemovedReason.Expired)
            {
                Log.Add(key);
            }
        }

        public void PutObjectToCache(string key, MyClass value)
        {
            CacheItemRemovedCallback onRemoveFunction;
            onRemoveFunction = new CacheItemRemovedCallback(NotifyFunction);

            Cache.Insert(key, value, null, DateTime.Now.AddMinutes(30), Cache.NoSlidingExpiration, CacheItemPriority.Normal, onRemoveFunction);
        }
    }
В этом примере функция NotifyFunction будет являться функцией обратного вызова. Она должна соответствовать строго определенному делегату CacheItemRemovedCallback:
       public delegate void CacheItemRemovedCallback(string key, object value, CacheItemRemovedReason reason);
В методе PutObjectToCache мы создали объект данного делегата, связали его с функцией NotifyFunction и указали в качестве последнего параметра метода Cache.Insert. Теперь эта функция связана с объектом в кэше и будет вызвана через 30 минут после выполнения метода PutObjectToCache(в соответствии с указанным сроком хранения объекта в кэше).
В эту функцию будет переданы ключ и значение удаленного объекта, а также причина, из-за которой он был удален из кэша. Причина является значением из перечисления CacheItemRemovedReason и может принимать следующие значения:
  1. Removed – объект был программно удален из кеша;
  2. Expired – объект был удален из кэша по истечению установленного времени хранения(абсолютного или относительного);
  3. Underused – объект был удален внутренним механизмом ASP.NET для освобождения системной памяти;
  4. DependencyChanged – объект был удален из кэша в связи с изменением одной из установленных для него зависимостей.
Стоит отметить, что функция обратного вызова вызывается после удаления объекта из кэша и повлиять на этот процесс внутри это функции разработчик не сможет. Впрочем, если необходимо, можно внутри этой функции еще раз помещать данный объект в кэш.


Очистка кэша
В программном интерфейсе объекта Cache не предусмотрен метод для полной очистки кэша. Однако при необходимости можно воспользоваться следующим способом:
        foreach (DictionaryEntry entry in Cache)
        {
             string key = entry.Key.ToString();
             Cache.Remove(key);
        }
Впрочем полностью очистить кэш разработчик все-таки не сможет. Я упоминал раньше, что в кэше, помимо объектов помещенных разработчиком, хранится ряд системных объектов, используемых механизмами ASP.NET, и доступ к ним надежно закрыт от разработчиков.
Выше был приведен пример перебора всех объектов, находящихся в кэше. Мы использовали это для их удаления, однако также это может быть полезно и необходимо для многих других задач.


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

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

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