Windows Azure: In-Memory Distributed Cache

в 8:50, , рубрики: cache, microsoft, windows azure, Веб-разработка, Облачные вычисления, метки: , , ,

Совсем недавно Microsoft презентовал большой пакет обновлений для платформы Windows Azure. В список нововведений входило долгожданное обновление Windows Azure Caching. Раньше разработчик сталкивался с некоторыми трудностями при работе с системой распределенного кэширования — кэш работал очень медленно. Так как Windows Azure хранил данные кэша на отдельных серверах, то на запрос выборки данных уходило порядка 30-100 мс, что является непозволительным для системы, ускоряющей доступ к данным.
Итак, что же изменили в системе кэширования?
Microsoft решила не отставать от своих конкурентов и добавила поддержку In-Memory Caching. Теперь данные хранятся не на отдельном сервере, а в оперативной памяти сервера и, соответственно, доступ к данным происходит моментально. Попробуем реализовать новый кэш и сравнить скорость работы со старым на примере ASP.NET MVC 3 (C#).

Caching Provider

Для доступа к кэшу нам понадобится провайдер. Чтобы реализовть провайдер, создадим для него интерфейс с необходимыми методами:

public interface ICacheProvider
    {
        void RemoveFromCache(string key); 
        T GetFromMethod<T>(string cacheKey, TimeSpan cacheDuration, Func<T> method); 
        T GetFromMethod<T>(string cacheKey, Func<T> method);
    }

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

Следующим шагом реализуем сам провайдер. Здесь не описывается процесс установки Windows Azure SDK 1.7, подразумевается, что у разработчика стоят все необходимые библиотеки и компоненты для разработки под Windows Azure

Отметим несколько основных моментов, которые следует учесть при реализации провайдера:

  • Кэш не имеет функционала полной очистки, поэтому нужно предусмотреть возможность инвалидации старых данных при выпуске обновлений;
  • Запись в кэш может производить только один поток, во избежание ошибок;
  • Windows Azure Caching не бесплатный функционал, поэтому нужно сократить обращения к кэшу до минимума.

Сам провайдер будет выглядеть так:


 public class AzureCacheProvider : ICacheProvider
        {
            //Необходимо для инвалидации старого кэша при новом релизе
            private readonly string _prefix = StaticHelper.GetCurrentVersion();
            private readonly object _locker = new object();
            private readonly DataCache _cache;
            public AzureCacheProvider()
            {
                lock (_locker)
                {
                    {
                        var factory = new DataCacheFactory();
                        _cache = factory.GetDefaultCache();
                    }
                }
            }
            private void RemoveFromCache(string key, DataCache cache)
            {
                lock (_locker)
                {
                    cache.Remove(_prefix + key);
                    HttpContext.Current.Items.Remove(_prefix + key);
                }
            }
            private T GetFromCache<T>(string key, TimeSpan expiration, Func<T> method, DataCache cache)
            {
                object cacheItem = HttpContext.Current.Items[_prefix + key];
                if (cacheItem == null)
                {
                    cacheItem = cache.Get(_prefix + key);
                    if (cacheItem != null)
                        HttpContext.Current.Items[_prefix + key] = cacheItem;
                }
                if (cacheItem == null)
                {
                    lock (_locker)
                    {
                        cacheItem = cache.Get(_prefix + key);
                        if (cacheItem == null)
                        {
                            cacheItem = method();
                            if (cacheItem != null)
                            {
                                HttpContext.Current.Items[_prefix + key] = cacheItem;
                                cache.Put(_prefix + key, cacheItem, expiration);
                            }
                        }
                    }
                }
                return (T)cacheItem;
            }
            public void RemoveFromCache(string key)
            {
                RemoveFromCache(key, _cache);
            }
            public T GetFromMethod<T>(string cacheKey, TimeSpan expirationSeconds, Func<T> method)
            {
                return GetFromCache(cacheKey, expirationSeconds, method, _cache);
            }
            public T GetFromMethod<T>(string cacheKey, Func<T> method)
            {
                return GetFromMethod(cacheKey, TimeSpan.FromMinutes(15), method);
            }
        }

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

Конфигурация роли

После написания провайдера нужно настроить кэш. В версии SDK 1.7 в свойствах роли появилась новая вкладка Caching.

image

На данной вкладке нужно включить поддержку кэша (Enable Caching) и установить количество выделяемого объема памяти под наши данные (например: Сo-located Role: Cache Size 30%)

Web.config

Последней подготовкой будет добавление новых секций в Web.config. Если в самом начале файла отсутствует раздел configSections, то его необходимо добавить. После чего внутрь поместить следующий код:

<section name="dataCacheClients" type="Microsoft.ApplicationServer.Caching.DataCacheClientsSection, Microsoft.ApplicationServer.Caching.Core" allowLocation="true" allowDefinition="Everywhere"/>

В конец Web.config добавляем описание секции dataCacheClients:

<dataCacheClients>
  <tracing sinkType="DiagnosticSink" traceLevel="Error" />
  <dataCacheClient name="default">
    <autoDiscover isEnabled="true" identifier="Указываем здесь название Web-роли, в которой настраивали кэш на предыдущем этапе" />    
  </dataCacheClient>
</dataCacheClients>

На этом настройка Windows Azure Caching закончена. Перейдем к проверке работоспособности.

Тестирование кэша

Для тестирования кэша создадим простенькую страничку, которая будет кэшировать текущее время:

<h1>Cache test</h1>
Before: <%=DateTime.Now.Millisecond%> ms
<%=Facade.Instance.Cache.GetFromMethod("currentTime", () => DateTime.Now)%>
After: <%=DateTime.Now.Millisecond%> ms

<%using(Html.BeginForm("ClearCache","Admin"))%>
<%{%>
    <input type="submit" value="Clear Cache"/>
<%}%>

Facade.Instance.Cache в коде выше является экземпляром объекта AzureCacheProvider , полученным с помощью IoC.
Действие ClearCache выглядит достаточно просто:


 public ActionResult ClearCache()
        {
            Facade.Instance.Cache.RemoveFromCache("currentTime");
            return View("admin");
        }

Результаты

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

Cache test
Before: 553 ms
6/10/2012 11:09:40 AM
After: 569 ms

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

Cache test
Before: 508 ms
6/10/2012 11:09:40 AM
After: 508 ms

Выборка данных из нового In-Memory Distributed Cache занимает меньше 1мс. По скорости, данный кэш не уступает InProc кэшу.

Как было раньше?

Чтобы узнать, как было раньше, необходимо откатить SDK до версии 1.6 и провести небольшую модификацию Web.config. Заменить секцию dataCacheClients на:

<dataCacheClients>
    <dataCacheClient name="default">
      <hosts>
        <host name="[Ваш ID].cache.windows.net" cachePort="22233" />
      </hosts>
      <securityProperties mode="Message">
        <messageSecurity authorizationInfo="[Ваш ключ]"></messageSecurity>
      </securityProperties>
    </dataCacheClient>  
</dataCacheClients>

После компиляции проекта и открытия тестовой страницы видим следующие результаты:

Cache test
Before: 521 ms
6/10/2012 11:09:40 AM
After: 550 ms

Т.е. 29 мс на добавление записи в кэш и после обновления странички:

Cache test
Before: 501 ms
6/10/2012 11:09:40 AM
After: 533 ms

32 мс на извлечение записи из кэша.

Итог

Последнее нововведение Microsoft решило одну из главных проблем Windows Azure — проблему распределенного кэширования. Теперь использовать распределенный кэш можно, не опасаясь за падение производительности приложения, а те, кто использовал для этого другие средства внутри облака Azure, могут вновь вернуться к функционалу предоставляемому Windows Azure API.

Автор: gurfing

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js