Кэширование ресурсовКэширование ресурсовГоворя о кэшировании ресурсов, мы обычно подразумеваем что-то, что было сериализировано в файловый формат и использовано в конечной точке. Это может быть что угодно, начиная с сериализированных объектов (например, XML, JSON) и заканчивая изображениями и видео. Для реализации такого кэширования можно попробовать использовать, например, http заголовки и мета-теги чтобы повлиять на механизм кэширование браузера. Но, необходимо понимать, что очень часто данные Вами указания не будут учтены. Из этого следует, что мы далеко не всегда можем успешно кэшировать медленно меняющийся веб контент на стороне клиента – во всяком случае, с гарантией производительности и устойчивости при нагрузке. Однако, вместо того чтобы переместить статические ресурсы назад на веб сервер, для большинства из них мы можем использовать сети доставки контента. Давайте подумаем о том пути, который контент проходит от веб серверов до конечного клиента, и о том, сколько времени это занимает. Одной из возможностей увеличения скорости прохождения данного пути есть создание особых точек, в которых контент будет не только кэшироваться – но, что более, важно, он станет ближе к географически распределенным конечным пользователям. Сервера используемые для распространения контента теперь известны как сеть доставки контента. Использование такой сети является очень эффективным, особенно если нам нужно покрыть большую географическую область. В ранние дни интернета идея внедрения рассредоточенного кэширования ресурсов была достаточно нова и компании вроде Akami Technologies преуспели в продаже сервисов, направленных на расширение веб сайтов за счет сетей доставки контента. Двадцать лет спустя эта стратегия так же важна для мира, в котором Веб сводит вместе людей, которые физически находятся в разных точках планеты. В случае с Windows Azure, Майкрософт предоставляет свою сеть доставки контента - Windows Azure Content Delivery Network (CDN). В большинстве случаев, во время развертывания веб сайта, кажется само собой разумеющимся, что все файлы должны располагаться на серверах сайта. В веб роли Windows Azure содержимое сайта развертывается пакетно – раз и все, готово. Стоп, а последние маркетинговые материалы не попали в пакет для развертывания, пора проводить повторное развертывание. Сейчас обновление контента означает повторное развертывание пакета. Конечно, можно развернуть его на отладочном окружении и потом сделать смену IP адресов, но этот процесс пройдет не без задержки или возможных неприятностей для пользователей. Простой путь для создания обновляемого веб кэша – это хранение большей части контента в Windows Azure Storage и указывать всеми ссылками (URI) на контейнеры Windows Azure Storage. Однако, по разным причинам, может быть необходимо оставить контент с веб ролями. Одним из путей для реализации такого сценария есть размещение всего контента в службе Windows Azure Storage, а по необходимости, перемещать его в локальное хранилище ресурсов. Кэширование в оперативную памятьБез сомнения, внедрение плана кэширования потока вывода (сгенерированный HTML, который нет необходимости генерировать снова, и который может быть просто так отправлен клиенту) и кэширования данных поможет улучшить производительность и масштабируемость сервиса. Сложный момент во внедрении стратегии кэширования на сайте приходится на то, как определить, что именно нам нужно кэшировать и как часто этот контент должен обновляться, с одной стороны, и что остается динамическим и генерируется при каждом запросе, с другой. Помимо стандартных возможностей, которые предоставляет Microsoft .NET Framework для кэширования потока вывода, Windows Azure предоставляет распределенный кэш под названием Windows Azure AppFabric Cache. Распределенный кэшРаспределенный кэш помогает решить сразу несколько проблем. Например, хотя кэширование всегда рекомендуется для улучшения производительности сайта, использование состояний сессии обычно противопоказано, несмотря на предоставления контекстного кэша. Причина в том, что состояние сессии требует привязки клиента к серверу, что негативно влияет на масштабирование, или же синхронизации с серверами в ферме, что, по общему мнению, считается – и не без оснований – ограниченным и проблематичным подходом. Проблема с состоянием сессии решается использованием стабильного и емкого распределенного кэша. Он дает возможность серверам получить данные без необходимости постоянно выходить за пределы решения и одновременно предоставляет механизм для записи данных и их распространения по кэш клиентам. Такое решение дает разработчику богатый контекстный кэш, оставляя возможность масштабировать веб ферму. Одно из преимуществ AppFabric Cache – это то, что его можно использовать с состояниями сессий, изменив всего несколько настроек. Другим преимуществом является то что есть простой в использовании программный API. Если рассматривать сценарий использования службы AppFabric Cache для кэширования состояния сеанса, то у многих может возникнуть вопрос – насколько это целесообразно? Многие разработчики привыкли, и не безосновательно, использовать для хранения состояния сеанса .net веб приложения MS SQL базу данных. Данное решение имеет ряд преимуществ, перечислять которые сейчас мы не будем. Оно довольно просто в настройке, хотя использование SQL Azure для хранения состояния сеанса пользователя требует дополнительной настройки. Но, проведя серию экспериментов с хранением состояния сеанса в MS SQL базе данных, с одной стороны, и хранением его в AppFabric Cache, с другой, я смог достичь увеличения производительности приложения до 30% просто за счет выбора другого механизма хранения состояния сеанса. Таким образом, потратив 5 минут рабочего времени, мы можем увеличить производительность приложения в среднем на 30%. Давайте рассмотрим на примере каким образом мы можем использовать AppFabric Cache для хранения состояния сеанса нашего веб сайта. Первым делом нужно зайти в панель управления своим аккаунтом Windows Azure и создать новый кэш, как это показано на рисунке. Далее нам необходимо сделать некоторые изменения в файле web.config нашего сайта. Чтобы понять что именно нужно изменить, мы должны нажать на кнопку «view client configuration» в панели управления подпиской Windows Azure. Как только мы это сделаем, мы увидим окно с подробной инструкцией по тому, какие модификации нам необходимо проделать. Итак, первым делом, нам необходимо добавить следующую конфигурацию в секцию configSections. Важно помнить, что мы ни в коем случае не должны заменять саму секцию configSections, нам просто нужно ее дополнить.
Теперь мы должны указать точку для подключения к нашему кэшу. Делается это путем добавления во все тот же файл web.config следующих конфигурационных элементов:
Тут стоит обратить внимание на одну вещь, платформа Windows Azure позволяет нам общаться с нашим кэшем как по открытому, так и по шифрованному каналу. Выбор каждого из них осуществляется путем смены порта подключения к службе AppFabric Cache. Если мы используем порт 22233, то наши данные будут передаваться по открытому каналу. Если мы используем порт 22243, то наши данные будут передаваться по шифрованному каналу.
Также вам необходимо включить в свой проект библиотеку Microsoft.Web.DistributedCache.dll, которая идет в поставке WindowsAzureSDK. Для этой библиотеки обязательно указать Аналогично стоит отметить, что еще одной полезной возможностью AppFabric Cache есть возможность легко и просто использовать его не только для хранения состояния сеанса, но также и для кэширования потока вывода. Выполнить настройку такого кэширования представляет не больше сложностей, чем настройка хранения состояния сеанса в кэше.
Если вы пройдете все шаги, описанные для добавления в ваш проект кэша для хранения состояния сеанса, но, вместо добавления конфигурационной секции хранения состояния сеанса добавите данную секцию, то ваш кэш будет использован для кэширования потока вывода. Еще одно важное замечание – мы используем распределенный кэш, т.е. доступ к нему одинаковый для всех экземпляров виртуальных машин, которые обслуживают нашу веб роль. Допустим, какой-то из пользователей сгенерировал запрос, он попадет на случайный экземпляр веб роли. Данный экземпляр сгенерирует пользователю ответ и закэширует его. До тех пор, пока не истекло время жизни этого ответа, любой запрос отправленный любым пользователем на любую веб роль будет обработан используя закэшированный вариант ответа. Этот момент очень важен, так как он избавляет нас от потребности генерировать ответы на аналогичные запросы каждым экземпляром веб роли.
Таблица 1 – Добавление контента через API кэша
Таблица 1 четко показывает, что даже глядя на базовые элементы API, можно увидеть разницу. Создание слоя перенаправления для отправки запросов поможет улучшить гибкость кода в приложении. Естественно, потребуется поработать над организацией легкого доступа к расширенным возможностям трех видов кэша, но выгоды перекроют все усилия для внедрения необходимого функционала. Взгляд на кэш с другой стороныКак и много других вещей в информационных технологиях (и, наверное, других сферах), готовое решение – это смесь идеальных технических решений, видоизмененных фискальными реалиями. Поэтому если вы всего лишь используете Windows Server 2008 R2 AppFabric Caching, все равно остаются причины использовать локальное кэширование, предоставленное в System.Web.Caching. В первом приближении я смог создать обертки вызовов (wrapped the calls) к каждой из библиотек кэширования и представил для каждой свою функцию, по типу AddtoLocalCache(ключ, объект) и AddtoSharedCache(ключ, объект). Однако это означает, что каждый раз, когда будет необходимо провести операцию кэширования, разработчик будет вынужден принимать довольно личное и размытое решение насчет того, где, а точнее в каком кэше, она должна состояться. Данный подход сложен в поддержке и быстро дает трещину при использовании его в больших командах, а также однозначно приводит к непредвиденным ошибкам, потому что разработчик мог решить добавить объект в неподходящий кэш или добавить объект в один кэш, а затем случайно считать его из другого. Таким образом будет происходить много ненужных выборок, так как данные не будут найдены в кэше или же будут находиться в неправильном кэше во время выборки. Это приводит к ситуациям, когда низкая производительность вопреки ожиданиям объясняется тем, что операции добавления были проведены в одном кэше, а выборки – в другом, только по той причине, что разработчик запутался в кэше или допустил опечатку. Более того, во время правильного планирования системы, такие типы данных (сущности) должны быть определены наперед и заодно должны быть обозначены идеи, где и какая сущность будет использоваться, вместе с требованиями к целостности (особенно для серверов с распределенной нагрузкой (load-balanced)) и актуальности данных. Из этого можно сделать вывод, что решения куда кэшировать данные (в общий кэш или нет) и когда их обновлять могут быть сделаны раньше своего времени и стать частью определения системы. Если вы используете кэширование, у вас должен быть четкий план. Очень часто оно просто бесцельно добавляется в конце проекта, но кэширование заслуживает такого же времени и важности при планировании и проектировании приложения, как и любой другой аспект. Это особенно важно, когда вы имеете дело с облаком, потому что плохо продуманные решения приведут к увеличению стоимости и неэффективному использованию ресурсов. Когда Вы определяете типы данных, которые должны быть кэшированы, Вы должны сперва определить их жизненный цикл в приложении и в пользовательской сессии. С этой точки зрения быстро понимаешь, что было бы хорошо, если бы сущность сама могла умно кэшироваться, базируясь на своем типе. К счастью, реализация этой задачи легко воплощается в жизнь при помощи пользовательских атрибутов (Custom Attribute). Мы пропускаем настройку кэшей в данной статье, так как для этого достаточно других материалов. В примере для библиотеки кэширования мы просто создаем статический класс со статическими методами. В других реализациях есть много причин для того, чтобы сделать то же самое с использованием экземпляров объектов, но для простоты примера, сделаем их статическими. Мы объявим перечисление, чтобы указать место для кэширования, и класс, который наследуется от класса Attribute, для реализации пользовательского атрибута, как показано в примере 1. Пример 1 – Объявление перечисления и класса для реализации пользовательского атрибута:
Передача местоположения в конструкторе помогает в дальнейшем его использовании в коде. В дополнение к этому, мы будем использовать метод только для чтения с целью получения значения, где именно кэшируется данный объект, так как это значение нужно нам для оператора выбора. В библиотеке CacheManager мы создали несколько приватных методов для добавления в два кэша.
В реальной реализации нам, скорее всего, понадобится больше информации (например, название кэша, частота обновления, зависимости, т.п.), но хватит и такого примера. Главная публичная функция для добавления контента в кэш – это шаблонный метод, что облегчает нам определение кэша по типу, как показано в примере 2.
Мы используем переданный тип для того, чтобы получить пользовательский атрибут и определить тип нашего пользовательского атрибута с помощью метода GetCustomAttribute(тип, тип). После этого мы просто создаем запрос к свойству для чтения и оператору выбора, и мы успешно перенаправили выборку к провайдеру подходящего кэша. Чтобы быть уверенным, что все будет работать правильно, нужно правильно описывать определения классов.
Вся инфраструктура приложения уже настроена, теперь ее можно использовать в коде приложения. Мы просто открываем файл default.aspx.cs, чтобы создать примеры запросов и добавить код для создания типов, установить несколько значений и добавить их в кэш:
Наши названия типов однозначно указывают, где будут кэшироваться данные. Однако, можно было бы изменить названия типов и это было бы менее однозначно, при этом кэширование бы управлялось с помощью проверки пользовательского атрибута. Использование такого подхода спрячет от разработчика детальную информацию о том, куда кэшируются данные, а также другую информацию, связанную с настройками кэша. Таким образом, эти решения остаются на совести той части команды, которая создает словари данных и прописывает общий жизненный цикл данных. Обратите внимание, что тип передается в запросах к AddToCache(string, T). Реализация остальных методов для класса CacheManager (т.е. GetFromCache) потребует использования такого же шаблона, какой использован для метода AddToCache. Балансирование стоимости с производительностью и масштабируемостьюWindows Azure предоставляет всю необходимую инфраструктуру для того, чтобы помочь Вам в реализации механизма кэширования. Ключевой момент к созданию хорошего проекта и, как следствие, хорошей реализации, - это балансировка стоимости с производительностью и масштабируемостью. Если вы сейчас работаете над созданием нового приложения и собираетесь в нем использовать кэширование – потратьте время и постройте дополнительный слой передачи данных. Это дополнительная работа, но с появлением новых возможностей, таких как кэширование AppFabric, вам будет легче эффективно и правильно включать их в свое приложение. Автор статьи: Антон Бойко. |