Сервис кэширования предоставляет решение по кэшированию для приложений Windows Azure. Кэширование – это широко известный прием для улучшения производительности приложений и уменьшения нагрузки на хранилища данных.
Методология планирования мощностей
Когда вы принимаете решение, что ваше приложение Windows Azure может получить выгоду от кэширования, вы приступаете к планированию мощностей кэша. Хоть это и возможно - провести некоторое планирование мощностей через прямое тестирование предоставленных кэшей Windows Azure – этот подход не всегда можно применить на оценочной стадии.
Следующие шаги представляют собой систематизированный подход для определения ваших требований к кэшированию:
- Шаг первый: понять узкие места и определить кандидатов на кэширование.
- Шаг второй: оценить текущие схемы рабочей нагрузки.
- Шаг третий: понять физическую инфраструктуру и аппаратные ресурсы.
- Шаг четвертый: окончательно сформировать требования к производительности в соглашении об уровне услуг для всех приложений.
- Шаг пятый: определить подходящие возможности и настройки конфигурации.
Данная статья предлагает примеры прохождения этих шагов. Давайте рассмотрим такое простое приложение как интернет магазин. Хочу сразу отметить, что хоть вы и можете использовать один и тот же кэш для нескольких приложений Windows Azure, рекомендуется использовать один кэш на приложение для улучшения разделения этих приложений в будущем. Это предоставит вам наибольший уровень гибкости в управлении и увеличит возможности кэша с ростом требований вашего приложения.
Шаг первый: понять узкие места и определить кандидатов на кэширование
Первым делом определите, какие данные вы хотите кэшировать. Один из путей, как это можно сделать, это предоставить вашему запущенному на машине разработчика приложению Windows Azure тестовую рабочую нагрузку. Локальное тестирование позволяет вам использовать такие инструменты, как SQL Server Profiler,Performance Monitor, Visual Studio testing и т.д. Это даст вам представление о наиболее вызываемых методах, самых медленных сервисах и операциях баз данных, а также ряд других характеристик, по которым можно определить общий уровень производительности системы. Так как данная информация собирается локально на одной машине, она может служить основой для квалифицированного предположения относительно поведения приложения в режиме рабочей нагрузки. Естественно, данный путь дает только грубую оценку, поскольку тестирование проходит не в облаке и невозможно применить полноценную нагрузку на приложение.
Более точная оценка может быть сделана с помощью просмотра данных диагностики из приложения на Windows Azure. Есть несколько способов, как можно собрать эти данные из ваших экземпляров ролей в Windows Azure. Во-первых, мы можем настроить Windows Azure Diagnostics для сбора логов IIS, логов событий Windows, счетчиков производительности, и других диагностических данных. С помощью пользовательского логирования или трассировки вы можете получать информацию о частоте вызовов методов, времени, которое они требуют на выполнение, или другие данные, уникальные для приложения. Этот путь дает более точную картину для поиска областей, которые могли бы больше всего выиграть от кэширования.
Давайте рассмотрим на примере как настроить Windows Azure Diagnostics для сбора необходимой информации. Настройка Windows Azure Diagnostics проходит в два этапа:
- Необходимо включить в проект саму службу Windows Azure Diagnostics.
- Необходимо настроить счетчики сбора производительности, логирование ошибок и т.д.
Включение в проект модуля Windows Azure Diagnostics проходит довольно просто. Сперва вы должны найти в проекте Windows Azure файл ServiceDefinition.csdef. Затем в нем необходимо указать, что для конкретной роли вы хотите использовать службу Windows Azure Diagnostics. Выглядит это следующим образом:
<?xml version="1.0" encoding="utf-8"?>
<ServiceDefinition name="MyHostedService"
xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition">
<WebRole name="WebRole1">
<Imports>
<Import moduleName="Diagnostics" />
</Imports>
</WebRole>
</ServiceDefinition>
Как видно из примера, импортировать модуль диагностики нужно для каждой роли, для которой вы хотите его использовать.
Как только мы импортировали модуль диагностики, мы можем начать настройку счетчиков производительности. Давайте разобьем этот процесс на несколько этапов.
Сперва настроим стандартные счетчики производительности, для примера возьмем показатель загруженности центрального процессора. Все изменения, если это не будет оговорено отдельно, мы будем проводить в рамках метода OnStart нашей Windows Azure роли.
public override bool OnStart()
{
return base.OnStart();
}
Первым делом нам нужно получить текущие параметры конфигурации службы диагностики.
var config = DiagnosticMonitor.GetDefaultInitialConfiguration();
Теперь добавим наши стандартные счетчики производительности.
config.PerformanceCounters.DataSources.Add(new PerformanceCounterConfiguration()
{
CounterSpecifier = @"\Processor(_Total)\% Processor Time",
SampleRate = TimeSpan.FromSeconds(5)
});
Далее необходимо установить с какой частотой мы будем сохранять показатели нашего счетчика в таблицу.
config.PerformanceCounters.ScheduledTransferPeriod = TimeSpan.FromMinutes(1.0);
И перезапустить службу диагностики с новыми параметрами конфигурации, где Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString – имя строки подключения к службе Windows Azure Storage.
DiagnosticMonitor.Start("Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString", config);
Теперь код метода OnStart выглядит таким образом:
public override bool OnStart()
{
var config = DiagnosticMonitor.GetDefaultInitialConfiguration();
config.PerformanceCounters.DataSources.Add(
new PerformanceCounterConfiguration()
{
CounterSpecifier = @"\Processor(_Total)\% Processor Time",
SampleRate = TimeSpan.FromSeconds(5)
});
config.PerformanceCounters.ScheduledTransferPeriod =
TimeSpan.FromMinutes(1.0);
DiagnosticMonitor.Start("Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString", config);
return base.OnStart();
}
Аналогичным образом мы можем добавлять любые другие стандартные счетчики производительности. Но, для полноты примера, давайте создадим собственный счетчик производительности.
Допустим, мы хотим знать какое количество раз вызывался конкретный участок кода. Для этого нам необходимо проделать похожий ряд операций. Во-первых, в файл ServiceDefinition.csdef необходимо добавить следующую конфигурационную секцию:
<?xml version="1.0" encoding="utf-8"?>
<ServiceDefinition name="MyHostedService"
xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition">
<WebRole name="WebRole1">
<Runtime executionContext="elevated" />
<Imports>
<Import moduleName="Diagnostics" />
</Imports>
</WebRole>
</ServiceDefinition>
Она укажет, что наш код будет исполняться под правами администратора системы. Теперь добавим следующий код в метод OnStart нашей роли:
if (!PerformanceCounterCategory.Exists("MyCustomCounterCategory"))
{
CounterCreationDataCollection counterCollection =
new CounterCreationDataCollection();
CounterCreationData operationTotal = new CounterCreationData();
operationTotal.CounterName = "MyCounter";
operationTotal.CounterType = PerformanceCounterType.NumberOfItems32;
counterCollection.Add(operationTotal);
PerformanceCounterCategory.Create(
"MyCustomCounterCategory",
"My Custom Counter Category",
PerformanceCounterCategoryType.SingleInstance, counterCollection);
}
Смысл этого куска кода очень прост. Сперва мы проверяем если ли у нас уже секция счетчиков производительности с нашим именем. Если таковой нет, то мы создаем новую коллекцию, создаем новый счетчик, добавляем его в коллекцию и создаем секцию счетчиков производительности.
Добавив такую проверку мы, в завершение этого этапа, должны зарегистрировать этот счетчик как источник данных, аналогично тому как мы это делали для стандартного счетчика производительности:
config.PerformanceCounters.DataSources.Add(
new PerformanceCounterConfiguration()
{
CounterSpecifier = @"\MyCustomCounterCategory\MyCounter",
SampleRate = TimeSpan.FromSeconds(5)
});
Наш метод OnStart, после добавления одного стандартного счетчика и одного собственного счетчика производительности будет выглядеть следующим образом:
public override bool OnStart()
{
var config = DiagnosticMonitor.GetDefaultInitialConfiguration();
config.PerformanceCounters.DataSources.Add(
new PerformanceCounterConfiguration()
{
CounterSpecifier = @"\Processor(_Total)\% Processor Time",
SampleRate = TimeSpan.FromSeconds(5)
});
if (!PerformanceCounterCategory.Exists("MyCustomCounterCategory"))
{
CounterCreationDataCollection counterCollection =
new CounterCreationDataCollection();
CounterCreationData operationTotal = new CounterCreationData();
operationTotal.CounterName = "MyCounter";
operationTotal.CounterType = PerformanceCounterType.NumberOfItems32;
counterCollection.Add(operationTotal);
PerformanceCounterCategory.Create(
"MyCustomCounterCategory",
"My Custom Counter Category",
PerformanceCounterCategoryType.SingleInstance, counterCollection);
}
config.PerformanceCounters.DataSources.Add(
new PerformanceCounterConfiguration()
{
CounterSpecifier = @"\MyCustomCounterCategory\MyCounter",
SampleRate = TimeSpan.FromSeconds(5)
});
config.PerformanceCounters.ScheduledTransferPeriod =
TimeSpan.FromMinutes(1.0);
DiagnosticMonitor.Start("Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString", config);
return base.OnStart();
}
Пускай вас не пугает громоздкость этого кода, во первых он очень логичен, последователен и понятен, а во вторых его легко можно разбить не несколько методов поменьше (которые будут использоваться повторно) и он станет намного короче.
Теперь возьмем наш кусок кода, количество обращений к которому мы хотим измерить. Пускай наш код выглядит как в примере, и мы будем подсчитывать количество вызовов кода, который находится в условном блоке.
public ActionResult Download(string path)
{
if (!string.IsNullOrEmpty(path))
{
// интересующий нас код
}
return View();
}
Все что нам теперь необходимо, это добавить экземпляр нашего счетчика и увеличивать его на единицу. В результате этого, наш метод будет выглядеть таким образом:
static PerformanceCounter MyCounter = new PerformanceCounter(
"MyCustomCounterCategory",
"MyCounter",
string.Empty,
false);
public ActionResult Download(string path)
{
if (!string.IsNullOrEmpty(path))
{
MyCounter.Increment();
}
return View();
}
Этот кусок кода не нуждается в особых комментариях. Как и говорилось выше, мы создали экземпляр счетчика и увеличили его значение на 1.
Аналогичным образом мы можем создавать любые наши пользовательские счетчики производительности, размещать их в произвольных участках кода и проводить необходимые нам замеры. Результаты всех счетчиков будут находиться в службе Windows Azure Storage в таблицах.
На рисунке показана часть таблицы, в которой будут сохранены ваши данные собранные службой диагностики. Как мы видим, в таблицу пишется следующая информация: идентификатор пакета развертывания, название роли, название экземпляра роли, название счетчика производительности и значение этого счетчика, а также много другой дополнительной информации.
Еще одним способом собрать диагностическую информацию из Windows Azure – это запись в таблицы Windows Azure Storage логов трассировки. Для этого нам необходимо совершить похожие шаги. Сперва нужно добавить модуль диагностики и подправить его конфигурацию, т.к. это мы уже проходили в прошлом примере, то я покажу только как будет выглядеть результирующий файл ServiceDefinition.csdef
<?xml version="1.0" encoding="utf-8"?>
<ServiceDefinition name="MyHostedService"
xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition">
<WebRole name="WebRole1">
<Runtime executionContext="elevated" />
<Imports>
<Import moduleName="Diagnostics" />
</Imports>
</WebRole>
</ServiceDefinition>
И метод OnStart нашей роли
public override bool OnStart()
{
var config = DiagnosticMonitor.GetDefaultInitialConfiguration();
config.Logs.ScheduledTransferPeriod =
TimeSpan.FromMinutes(1.0);
DiagnosticMonitor.Start("Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString", config);
return base.OnStart();
}
Теперь, мы должны просто вызывать метод Trace.WriteLine и записывать в лог трассировки свои собственные сообщения. Допустим, мы хотим записывать в лог трассировки сообщение каждый раз, когда наша роль worker обрабатывает очередное сообщение. Выглядеть это будет таким образом:
public override void Run()
{
Trace.WriteLine("Worker entry point called", "Information");
while (true)
{
Thread.Sleep(10000);
MyCounter.Increment();
Trace.WriteLine("Working " + MyCounter.RawValue.ToString(),
"Information");
}
}
В результате таких несложных манипуляций мы получим таблицу с сообщениями трассировки в службе Windows Azure Storage, аналогичную той, что изображена на рисунке. Как видим в ней отображено следующее: идентификатор пакета развертывания, название роли, название экземпляра роли, уровень сообщения, идентификатор сообщений, идентификатор процесса, идентификатор сообщения, текст сообщения, а также много другой дополнительной информации
Если в этом есть необходимость, то мы можем использовать службу диагностики для сбора данных с эмулятора Windows Azure. Тут сразу стоит отметить, что использовать данные эмулятора для замера абсолютной производительности категорически не рекомендуется, но есть определенный смысл в использовании данных производительности эмулятора для замера относительной производительности. Хотя данные относительной производительности тоже не всегда будут отражать действительность, но на них все равно можно опереться.
В завершение примера хочу только еще раз отметить, что служба Windows Azure Diagnostics создает довольно много избыточной нагрузки на ваши рабочие сервера. Данную службу категорически не рекомендуется использовать на рабочем окружении, она предназначена в основном для использования только в рамках тестового окружения.
Еще одним способом извлечения информации о производительности серверов есть использование удаленного рабочего стола или Windows Azure Connect, чтобы мониторить экземпляры ролей. И наконец, если ваше решение использует VM роль, можно настроить образ VM роли с предварительно установленными инструментами мониторинга, которые смогут предоставлять отчет о производительности приложения, ассоциированного с VM ролью.
С помощью данного анализа определите объекты базы данных, к которым вы часто обращаетесь, или определите медленные обращения к сервисам. Данные, возвращаемые такими базами данных или сервисами – это потенциальные кандидаты на кэширование. Временное хранение таких данных в кэше может улучшить производительность и снизить нагрузку на хранилища данных. В Windows Azure кэширование может быть использовано как часть общей стратегии по оптимизации использования сервисов.
Как только вы определили кандидатов на кэширование, полезно классифицировать эти объекты в три основные категории: данные об активности (activity data), данные, на которые ссылаются много источников (reference data) и данные ресурсов (resource data).
Их суть можно объяснить на таких примерах:
- Данные об активности содержат считываемые и записываемые данные, которые относятся к отдельному пользователю. Например, в интернет магазине корзина пользователя – это данные об активности. Она относится к текущей сессии пользователя и может часто меняться. Хотя важно предоставлять пользователю постоянный доступ к корзине, эти данные совсем не обязательно требуют постоянного хранения в базе данных. Из-за своей временной сущности, данные об активности – логичный кандидат на кэширование.
- Данные, на которые ссылаются много источников, содержат считываемые и записываемые данные, которые используются совместно несколькими пользователями или же несколькими экземплярами приложения. Эти данные часто считываются, но не часто меняются. Например, в интернет магазине каталог товаров – это данные, на которые ссылаются много источников. Каталог будет действителен на протяжении одного или больше дней, но к нему могут обратиться тысячи раз разные пользователи. Такие данные – также отличный кандидат на кэширование. Без какого-либо кэширования, каждый пользователь, который просматривает каталог, запрашивает данные из базы данных. Использование кэширования может уменьшить нагрузку на базу данных повторяющимися запросами на полу-статические данные. Из-за своей постоянности, эти данные – также логичный кандидат на кэширование.
- Данные ресурсов содержат считываемые и записываемые данные, которые совместно используются пользователями. Например, форум поддержки является таким типом данных, поскольку все пользователи могут им пользоваться.
Так как разные типы кэшированных данных будут иметь разные модели использования, классификация данных по вышеизложенным признакам может быть очень полезной. Например, классификация данных как данные, на которые ссылаются много источников, автоматически означает, что на них будет больше нагрузки считывания. Это также помогает определить время жизни данных, которое обычно меньше для данных, которые меняются чаще. Для разработчика, такое логическое разделение может подсказать области, которые могут быть инкапсулированы в коде. Это разделение может также указать на области, которым мог бы пригодиться отдельный кэш, когда текущее максимальное предложение кэша (4Гб) не достаточно для содержания всех кэшированных данных приложения. Но для простоты данного примера мы будем считаться, что у нас используется только один кэш.
Также рекомендуется создавать оценки для отдельных объектов, а затем агрегировать данные оценок. Таблица 1 показывает пример информации, собранной на данном этапе:
Таблица 1 – оценка кандидатов на кэширование
Объект | Категория | Пример постоянного хранилища |
Объекты корзины товаров | Данные активности | Нет |
Объекты настроек пользователя | Данные активности | SQL Azure |
Каталог продуктов | Данные, на которые ссылаются много источников | SQL Azure |
Лента форума | Данные ресурсов | Windows Azure Blob Storage |
Не обязательно находить данные для кэширования всех трех категорий. Вы можете прийти к выводу, что масштабирование и производительность вашего приложения могут быть улучшены кэшированием только пользовательской корзины. Главное на данном этапе – это определение лучших кандидатов для кэширования, используя имеющуюся информацию.
Шаг два: оценить текущие схемы рабочей нагрузки
Как только вы определили подходящие для кэширования данные, вы должны понять, как приложение в данный момент обращается к данным, а также рассмотреть связанные схемы нагрузки. К концу данного этапа вы должны прийти к грубой оценке требований к объему кэша, который вам необходим. Вы также должны будете лучше понимать, как происходит использование ваших данных и обращение к ним, что будет важно в дальнейшем.
Например, если вы выбрали свой каталог товаров в роль кандидата на кэширование, вы должны изучить, когда приложение получает данные каталога и как совершаются данные запросы. Базируясь на предыдущем этапе, вы знаете, что это данные, на которые ссылаются много источников, и поэтому они будут иметь в основном нагрузку на считывание данных.
Есть несколько путей, как можно лучше понять текущие схемы доступа к данным (data-access patterns):
- Внимательно просмотрите код, чтобы понять, где происходит доступ к данным и как часто это происходит.
- Используйте code profiler вроде того, что поставляется с Visual Studio. Он может предоставить частоту вызовов методов и схожие данные о производительности.
- Создайте средства контроля в коде вокруг определенных секций обращения к данным. Логируйте попытки доступа и связанные с ними данные о производительности.
- Используйте database profiler вроде SQL Server Profiler, чтобы отследить количество и длительность операций в базе данных.
Обратите внимание, что многие из данных приемов рекомендуются к применению на предыдущем этапе для определения, какие данные следует кэшировать. Однако на данном этапе вам интересны более точные цифры, которые можно использовать в будущем для планирования и расчета мощности и объема кэша.
Самая главная оценка на данном этапе – это среднее количество транзакций в секунду во время пиковой нагрузки. С помощью простого умножения вы можете просчитать количество транзакций в час. Количество транзакций в час необходимо для проверки соответствию квоты транзакций. Таблица 2 показывает пример информации, собранной на данном этапе для объекта корзина.
Таблица 2 – Количество транзакций по объекту корзина
Объект анализа | Корзина |
Среднее количество транзакций в секунду (пиковая нагрузка) | 250 |
Оценка транзакций в час (пиковая нагрузка) | 900 000 |
В этом примере приложение требует кэш, который позволяет совершать как минимум 900 000 транзакций в час. Предложение кэша в 512Мб удовлетворяет данному требованию.
Понимание частоты операций также важно для квоты пропускной способности, но это требует дополнительных данных о размерах объекта. Данный анализ должен проводиться для каждого типа объектов. Разные типы объектов будут иметь разные схемы доступа и разное среднее количество транзакций в секунду при максимальной нагрузке.
Чтобы понять требования к размеру кэша, необходимо иметь оценку максимального количества активных объектов каждого типа в кэше на один определенный момент времени. Эта оценка включает в себя баланс между частотой вставки и ожидаемым сроком жизни этих объектов. Рассмотрим пример.
В нашем примере веб приложения данные мониторинга показали, что на сайте может находиться пиковое количество одновременных пользователей в количестве 700 человек. Для каждого пользователя потребуется хранить информацию о состоянии его сессии, что означает, что нам потребуется 700 кэшированных объектов. Операционные данные показывают, что во время данного пикового периода могут прийти еще 100 новых пользователей в час, и это означает, что еще 100 пользователей могут появиться за 30 минут – наше время жизни данных. Дополнительно, некоторые пользователи могут закрыть свой браузер и начать новую сессию. Хотя они и будут оставаться одним и тем же пользователем, но потребуют под себя новую сессию. Для учета данного случая мы добавим еще 100 сессий. И, наконец, мы должны учесть любой прогнозируемый рост в течение последующих 6-12 месяцев (в размере 10%, т.е. 90 сессий). Таким образом, подсчет максимального количества активных объектов в кэше будет выглядеть, как показано в Таблице 3.
Таблица 3 - подсчет максимального количества активных объектов в кэше
Объект анализа | Корзина |
Пиковое количество одновременных пользователей | 700 |
Новые пользователи за время жизни (30 минут) | 100 |
Существующие пользователи, которые начнут новые сессии | 100 |
Прогнозируемый рост (10%) | 90 |
Общее количество активных объектов (максимальное): | ~1000 |
Это всего лишь пример мыслительного процесса. Другие объекты потребуют другие схемы и другие переменные для расчета. Например, если каталог товаров действителен целый день, то максимальное количество объектов каталога товаров в кэше в течение дня будет величиной постоянной.
Всегда рекомендуется определять конкретный и подходящий период обновления данных для кэшируемых объектов, хотя провайдер состояния сессии делает это за вас по умолчанию. Однако, если вы программно добавляете объект в кэш без указания времени жизни, время жизни по умолчанию составляет 48 часов.
Знание максимального количества объектов в кэше может вам помочь, только если вы знаете средний размер объекта. Обычно это представляет сложную проблему. Например, в случае корзины товаров, один пользователь может поместить в нее один товар, в то время как другой поместит двадцать товаров. Вам нужно понять лишь среднее количество. Как и большинство цифр в данном процессе, они не должны быть точными, но чем точнее вы сможете их определить, тем меньше конечный результат будет похож на догадку и больше походить на хорошо продуманный расчет.
Объекты хранятся в кэше в сериализированной форме. Так что для того, чтобы понять средний размер объекта, вам нужно рассчитать размер среднего сериализированного объекта. Размер сериализированного объекта равен сумме размеров сериализированного ключа объекта и сериализированного значения объекта. Хотя возможно использовать собственный механизм сериализации, кэширование использует класс NetDataContractSerializer для сериализации перед тем, как объект отправляется на хранение в кэш. Чтобы определить средний размер объекта, добавьте средства контроля в свой код, которые будут сериализировать объекты, а затем записывать их сериализированный размер.
Последующий пример кода направлен на попытку оценить средний размер отдельного объекта. Объект, который мы сериализируем, называется obj. Переменная length используется для записи его размера. Если есть проблема в использовании NetDataContractSerializer, вместо него используется BinaryFormatter. Вы можете завернуть данный код в метод для упрощения использования. В таком случае obj будет передаваться как параметр, а length будет возвращаться из метода.
C#
// requires following assembly references:
//
//using System.Xml;
//using System.IO;
//using System.Runtime.Serialization;
//using System.Runtime.Serialization.Formatters.Binary;
//
// Target object “obj”
//
long length = 0;
MemoryStream stream1 = new MemoryStream();
using (XmlDictionaryWriter writer =
XmlDictionaryWriter.CreateBinaryWriter(stream1))
{
NetDataContractSerializer serializer = new NetDataContractSerializer();
serializer.WriteObject(writer, obj);
length = stream1.Length;
}
if (length == 0)
{
MemoryStream stream2 = new MemoryStream();
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(stream2, obj);
length = stream2.Length;
}
Обратите внимание, что альтернативным подходом есть применение к кэшу нагрузочного тестирования. Вы могли бы сравнить количество добавляемых объектов с размером кэша, показываемого в Windows Azure Platform Management Portal. Обычно есть задержка в несколько минут перед тем, как портал начнет отражать результаты недавней активности кэша. С данными, собранными на этом этапе, вы можете начать формировать общие требования к количеству памяти в кэше. Это включает в себя такие задачи:
- Сфокусируйтесь на типе сущности (например, сущность корзина).
- Найдите сериализированный размер экземпляра объекта для этой сущности.
- Добавьте 500 байт, чтобы учесть средний размер дополнительных затрат на кэширование.
- Каждый объект округляется до ближайшего килобайта.
- Умножьте полученный размер на максимальное количество активных экземпляров объектов. Это предоставит вам требование к количеству памяти кэша для данного типа сущности.
- Повторите вышеуказанные шаги для каждого определенного типа сущности.
- Соберите полученный результат в общие требования к памяти кэша.
Для примера мы предоставим Таблицу 4. В зависимости от вашего сценария, вы можете принять решение о проведении этой оценки на уровне сущностей или на уровне приложения.
Таблица 4 – Требования к памяти кэша
Объект анализа | Данные активностей | Данные, на которые ссылаются другие источники |
Средний размер экземпляра объекта (после сериализации): | 10240 Bytes | 81920 Bytes |
Дополнительные затраты на каждый экземпляр объект (1 %): | 102 Bytes | 819 Bytes |
Привед��нный средний размер экземпляра объекта: | 10342 Bytes | 82739 Bytes |
Округление до ближайшего КБ: | 11264 Bytes | 82944 Bytes |
Максимальное количество активных экземпляров объектов: | ~1000 | ~1200 |
Требования к памяти кэша: | 10.7 MB | 94.9 MB |
Вместе все вышеуказанные оценки формируют первоначальные требования к памяти кэша. В данном примере общая сумма равна 106Мб. С такой первоначальной оценкой вы можете начать рассматривать другие требования, включая требования соглашения об уровне услуг, оптимизации кэша, и их соответствие с квотами кэширования, которые повлияют на принятие окончательного решения.
Во время создания оценок нужно помнить, что Windows Azure поддерживает гибкое масштабирование. В данном примере нагрузка на интернет магазин могла бы быть отдельно оценена для «обычных» дней и отдельно для «пиковых» дней. В дальнейшем можно было бы выбрать меньшее предложение кэша, которое бы использовалось большую часть дней года и затем могло бы быть увеличено до большего предложения во время наступления пиковых дней.
Шаг три: понимание бизнес требований для всех приложений
Перед тем, как прийти к окончательному решению о конфигурации, нужно понимать бизнес требования или же требования соглашения об уровне услуг (SLA). На данном этапе необходимо определить количество отдельных кэшей для вашего решения.
Например, у вас есть критически важное приложение в кэше и возможно вы бы хотели изолировать этот кэш от низкоприоритетных приложений. Теоретически, эти низкоприоритетные приложения могут иметь негативное влияние на критически важное приложение. Например, если другие приложения используют слишком много памяти в кэше, данные критически важного приложения могут быть выброшены из кэша. Если другие приложение превысят лимиты по транзакциям, пропускной способности или подключениям, кэш может стать недоступен до момента, когда квоты обновятся в следующем квотном часе. Из этих соображений, даже если вы можете использовать один кэш на 4Гб для всех приложений, вы можете захотеть иметь несколько отдельных кэшей, которыми можно управлять отдельно.
Другой важный аспект – это безопасность. Доступ в кэш осуществляется через комбинацию из URL сервиса и ACS ключа. Если приложение должно отделять свои данные от других приложений, это еще одна причина, чтобы иметь раздельные кэши. Если вы будете использовать несколько кэшей, то каждое приложение будет иметь свой URL сервиса и ACS ключ.
Шаг четыре: оптимизация процесса кэширования и его дополнительные настройки
Таблица 5 иллюстрирует множество свойств кэширования, а также множество связанных с ними соображений относительно планирования мощности кэша. Все эти свойства могут быть задействованы для оптимизации использования кэша.
Таблица 5 – Настройки свойств кэша и способы оптимизации процесса кэширования
Область | Описание | Влияние |
Сжатие | По умолчанию сжатие отключено. Если его включить, то кэшируемые объекты будут сжиматься до отправки в кэш, и восстанавливаться во время возвращения из кэша. | Сжатые объекты меньше, т.е. средний размер объекта меньше. Это влияет на общее количество памяти и пропускной способности, которые вам понадобятся для сценария. |
Локальный кэш | Локальный кэш использует память на кэш клиенте, чтобы временно хранить кэшируемые объекты. Объекты возвращаются локально до заданного таймаута, а затем следующий запрос вынимает обновленный объект из кэша. | В случае, если приложение часто запрашивает одни и те же ключи из кэша, локальный кэш может увеличить производительность приложения, исключая повторные обращения к кэшу. Также это может существенно уменьшить количество транзакций в кэше. |
Совокупность подключений | Включите совокупность подключений, чтобы непрерывно использовать один и тот же набор подключений в любом количестве экземпляров DataCacheFactory для одной и той же конфигурации кэша. | Совокупность подключений делает общими подключения между экземплярами DataCacheFactory, которые используют одну и ту же конфигурацию клиента. В данном случае количество подключений равно значению maxConnectionsToServer. |
Экземпляры DataCacheFactory | Если совокупность подключений отключена, объект DataCacheFactory ассоциируется с открытым подключением к кэшу. Каждый провайдер ASP.NET использует один объект DataCacheFactory. | Если совокупность подключений отключена, количество экземпляров DataCacheFactory влияет на количество подключений к кэшу, которые требуются приложению. |
Параметр maxConnectionsToServer | Параметр maxConnectionsToServer определяет количество подключений на экземпляр DataCacheFactory, или же указывает общее количество подключений в совокупности подключений. | Если совокупность подключений отключена, увеличение maxConnectionsToServer умножает количество подключений на число экземпляров DataCacheFactory. Если совокупность подключений включена, maxConnectionsToServer указывает общее количество подключений в этой совокупности. |
Сжатие
Сжатие может быть установлено либо в настройках фала конфигурации, либо программно, путем установки значения свойства DataCacheFactoryConfiguration.IsCompressionEnabled в true. Это продемонстрировано в следующем примере:
<dataCacheClient name="default" isCompressionEnabled="true">
Если вы решили включить сжатие, теоретически вы должны увидеть увеличение пропускной способности и уменьшение количества занимаемой памяти. Проблемой здесь является оценка эффекта от сжатия. Например, сжатие графического файла не принесет такого большого эффекта, как сжатие текстового файла. Также вы должны помнить, что массивы байтов меньше чем 1 Кб не будут сжиматься. Чтобы оценить эффект от сжатия, вы можете использовать ручные тесты соответствующих типов сущностей.
Локальный кэш
Функция локального кэша позволяет вам хранить значения, выбранные из кэша, в локальной памяти вашего кэш клиента. Это может быть сделано программно путем настройки свойства DataCacheFactoryConfiguration.LocalCacheProperties. Также его можно включить в файле конфигурации. Пример показывает секцию localCache в файле конфигурации приложения:
<dataCacheClient isCompressionEnabled="true">
<localCache
isEnabled="true"
sync="TimeoutBased"
objectCount="10000"
ttlValue="300" />
<!-- Other sections... -->
</dataCacheClient>
Локальный кэш уменьшает количество транзакций в соответствующем удаленном кэше. Однако, он уменьшает только количество транзакций чтения, которые повторяются в пределах установленного времени жизни кэша (time-to-live). В вышеуказанном примере, если бы тот же кэшированный объект считывался 100 раз в течение 300 секунд, заданных как время жизни для локального кэша, тогда только при первом вызове Get получит доступ в кэш. Все остальные вызовы будут возвращены от локально кэшированного объекта. Точный выигрыш в производительности и транзакциях зависит от частоты повторяющихся считывания из кэша и длинны периода времени жизни кэша.
Однако нужно помнить, что не все типы данных подходят для локального кэширования. Например, рассмотрим ситуацию, когда вы включили локальный кэш на провайдере состояния сессии для вашей пользовательской корзины в интернет магазине. Если ваша ASP.NET роль имела несколько экземпляров, пользователь мог добавить товар в корзину в одном экземпляре, затем перейти на страницу, которая отображает корзину в другом экземпляре. Если включен локальный кэш, возможно, что локально хранимое состояние сессии еще не было обновлено изменениями. Это временное противоречие в данном случае неприемлемо. Однако, если бы вы использовали локальный кэш для каталога товаров, это бы работало хорошо.
Подключения
Когда настроена совокупность подключений, один и тот же источник подключений доступен для всей конкретной конфигурации клиента. Количество общих подключений определяется параметром maxConnectionsToServer. Это не зависит от того, сколько объектов DataCacheFactory было создано. Таким образом формула для определения общего количества подключений с использованием совокупности подключений такова:
[значение параметра maxConnectionsToServer] * [количество экземпляров Azure роли]
Когда совокупность подключений отключена, каждый объект DataCacheFactory использует одно или больше подключений, основываясь на значении maxConnectionsToServer. Важно инициализировать и хранить ваши экземпляры DataCacheFactory, чтобы контролировать количество открытых подключений, а также добиться максимальной производительности.
Когда вы не используете совокупность подключений, количество подключений, необходимых для кэша, определяется такой формулой:
[количество экземпляров DataCacheFactory] * [значение параметра maxConnectionsToServer] * [количество экземпляров Azure роли]
По умолчанию maxConnectionsToServer равно 1. Обычно, вы не должны менять значение этого параметра, если только вы не можете доказать позитивный эффект от его увеличения. Есть вероятность, что большее значение может улучшить производительность, когда вы делите одни и те же объекты DataCacheFactory или DataCache между потоками. Например, если maxConnectionsToServer равно 2, тогда каждый объект DataCacheFactory использует два подключения.
Изменения в данного параметра могут быть сделаны программно, используя свойство DataCacheFactoryConfiguration.MaxConnectionsToServer. Или же вы можете изменить его в файле конфигурации таким образом:
<dataCacheClient maxConnectionsToServer="2">
В данном примере количество экземпляров Azure ролей тоже важно. Если у вас три экземпляра вашей веб роли и каждая создает объект DataCacheFactory, тогда у вас три подключения (при maxConnectionsToServer=1).
Шаг пять: выбор подходящего предложения кэша Azure, которое соответствует вашим требованиям
В нашем примере, для приложения интернет магазина, мы рассмотрели оценки таких объектов:
Таблица 6 – Оценки объектов приложения интернет магазина
Объект анализа | Данные активностей | Данные, на которые ссылаются другие источники |
Приведенный средний размер сериализированного объекта: | 11 KB | 81 KB |
Максимальное количество активных объектов: | ~1000 | ~1200 |
Общие требования к памяти: | 10.7 MB | 94.9 MB |
Оценка транзакций в час (пиковая нагрузка) | 18000 | 28800 |
Оценка пропускной способности в час (пиковая нагрузка) | 194 MB | 2279 MB |
В Таблице 7 мы рассмотрели агрегированные данные по нашему примеру и соответствующие им предложения по кэшу, действительные на ноябрь 2011 года. Обратите внимание, что пропускная способность оценена путем умножения транзакций в час во время пиковой нагрузки на средний размер объектов. Подключения определяются проектом приложения и настройками развертывания, как описано в предыдущем разделе. В данном конкретном случае они взяты произвольно.
Таблица 7 – Соотношение требований и предложений по кэшу
Ресурс | Агрегированная оценка | Предложение кэша |
Память | 206 MB | 128 MB |
Транзакции | 46800 транзакций/час | 128 MB |
Пропускная способность | 2473 MB/час | 256 MB |
Подключения | 3 | 128 MB |
В данном примере, каждая оценка по ресурсу соотносится со специфическим предложением кэша, основываясь на лимитах квот. Подходящее предложение кэша определяется выбором наибольшего предложения, необходимого для любого из ресурсов. В данном примере это предложение кэша на 256МВ.
В случае с обычным приложением, чаще всего требования по памяти должны предоставить вам достаточно квот по транзакциям и пропускной способности. В примере это не так именно для того, чтобы продемонстрировать, как важно рассматривать квоты по всем ресурсам на этапе планирования.
Именно сейчас важны наши предыдущие шаги по оптимизации. Например, представьте себе тот же сценарий, но с включенным сжатием. Если бы сжатие уменьшило средний размер объекта на 70%, новый расчет был бы таким:
Таблица 8 – Соотношение требований и предложений по кэшу (с включенным сжатием)
Ресурс | Агрегированная оценка | Предложение кэша |
Память | 33 MB | 128 MB |
Транзакции | 46800 Trans/hour | 128 MB |
Пропускная способность | 775 MB/hour | 128 MB |
Подключения | 3 | 128 MB |
С помощью уменьшения среднего размера объекта, прогнозируемая пропускная способность также уменьшилась. Это уменьшило необходимое предложение по кэшу с 256МВ до 128МВ.
Если ваши расчеты показывают, что у вас есть требования, которые превосходят максимальное предложение по кэшу, вы должны либо изменить количество данных, которые вы кэшируете, либо использовать какой либо алгоритм распределения/разнесения данных, чтобы разместить разные типы данных в разные кэши.
Инструмент для планирования мощностей кэша
Для удобства использования вышеизложенной методологии мы составили пример таблицы с автоматизированными расчетами (download ). Ее можно скачать здесь. Ячейки, отмеченные астерисками (*) следует изменять в зависимости от ваших ресурсов и планирования. Остальные расчеты будут сделаны автоматически.
В первой части таблицы указываются текущие квоты сервиса кэширования. Их нужно сравнивать с официальной таблицей действующих квот.
Второй раздел дает вам возможность ввести оценки по разным типам объектов.
Третий раздел оценивает требования к сети. Вам нужно только ввести значение среднего количества транзакций в секунду (пиковая нагрузка), а количество транзакции в час и пропускную способность в час за вас пересчитает таблица.
В четвертом разделе оценивается количество соединений, необходимых вашему приложению Windows Azure.
Последний раздел собирает все требования для каждого ресурса и затем рассчитывает предложение кэша, которое подходит к данным требованиям.
Мониторинг действующих требований к мощности кэша
Ранее была представлена методология для определения первоначальной оценки подходящего предложения кэша. Но вы должны понимать, что эта оценка может меняться в зависимости от тестирования и действующего мониторинга.
Планирование мощности кэша никогда не будет точной наукой с точными результатами. Многие из цифр, которые используются при оценке, и сами являются оценками. Также схемы использования приложения могут меняться с течением времени. Из-за этого вы должны уметь сопоставлять свои оценки кэширования с фактическим его использованием в конкретной реализации.
Первый подход – это использование статистики на Windows Azure Platform Management Portal. Выбрав свой кэш, используйте окно свойств, чтобы просмотреть его текущий размер и его пиковый размер. Эти данные не обновляются в реальном времени, поэтому их обновление может занять около 15 минут. Если текущий или пиковый размер близки к размеру кэша, то вы можете испытывать вытеснение наиболее давно использовавшихся данных с продолжительным использованием кэша. Однако, эта статистика может быть неверной. Если вы не задали время жизни данных, добавляемых в кэш, по умолчанию оно устанавливается в 48 часов. Всегда лучше определить подходящее время жизни для данных, которые вы кэшируете, чтобы иметь возможность увидеть «настоящий» пиковый размер кэша.
В данный момент на портале нет возможности просмотреть статистику по транзакциям, пропускной способности или подключениям, но вы также должны следить за этими показателями. Если вы превысите одну из этих квот, API кэша выдаст DataCacheException с ErrorCode установленным в DataCacheErrorCode.RetryLater и SubStatus установленным в DataCacheErrorCode.QuotaExceeded. Вам необходимо логировать все исключения с помощью Windows Azure Diagnostics. Увидев такое исключение, вы сможете понять, квоту какого ресурса вы превысили.
Например, вот сообщение об ошибке на превышение квоты подключений:
Microsoft.ApplicationServer.Caching.DataCacheException: ErrorCode<ERRCA0017>:SubStatus<ES0009>:There is a temporary failure. Please retry later. (The request failed, because you exceeded quota limits for this hour. If you experience this often, upgrade your subscription to a higher one). Additional Information : Throttling due to resource : Connections.
Как указывает сообщение об ошибке, один из путей решения проблемы – это апгрейд вашего предложения кэша до квот, которые будут удовлетворять вашим изменившимся требованиям.