Want create site? Find Free WordPress Themes and plugins.

Fala Galera,

Hoje irei postar sobre como podendo integrar o Redis NoSQL para ser utilizado como cache distribuído em nossas aplicações ASP.NET.

O Redis é um banco de dados NoSQL de alta performance, in-memory, data estruturado e aceita diversos tipo como hashs, strings, lists, sets e ordered sets e muito mais.

As vantagens de se usar o Redis são:

  • Simples de Configurar
  • Fácil de Usar
  • Alta performance

Configurando o Redis no Azure

Para configurar o Redis no Azure, vamos entrar no painel de controle do Azure portal.azure.com e crie um novo serviço do Redis conforme figura abaixo:

Redis

Redis2

Com o serviço do Redis criado, o Azure irá fornecer o endereço de acesso e uma chave de acesso. É com isso que iremos configurar o nosso cache distribuído.

Integrando o cache distribuído ao ASP.NET MVC

Com o serviço do Cache criado, vamos integrar nosso serviço de Cache ao ASP.NET MVC.

Vamos criar um projeto MVC no Visual Studio conforme figura abaixo:

Redis3

Escolha o Template MVC e confirme a operação

Para que possamos usar o Redis em um projeto ASP.NET MVC devemos adicionar a biblioteca ServiceStack.Redis. Essa biblioteca se encontra no NuGet então basta executar o comando abaixo no Package Manager Console e assim poderemos usar o Redis em nosso projeto.

Install-Package ServiceStack.Redis

Com o pacote instalado vamos criar uma classe chamada CacheManager que encapsulará a leitura e gravação dos objetos no Cache.

public class CacheManager
   {
       private static IRedisClient cache;
       private static string host = "aspnet-cache.redis.cache.windows.net";
       private static int port = 6380;
       private static string password = "<SUA CHAVE>";

       private static IRedisClient Cache
       {
           get
           {
               if (cache == null)
                   cache = GetConnection();

               return cache;

           }
       }

       private static IRedisClient GetConnection()
       {
           RedisEndpoint redisConfig = new RedisEndpoint();
           redisConfig.Host = host;
           redisConfig.Ssl = true;
           redisConfig.Port = port;
           redisConfig.Password = password;

           RedisClient client = new RedisClient(redisConfig);

           return client;
       }

       public static bool Set<T>(string key, T value, DateTime? expireAt = null)
       {
           if (expireAt.HasValue)
               return Cache.Set<T>(key, value, expireAt.Value);

           return Cache.Set<T>(key, value);
       }

       public static T Get<T>(string key)
       {
            return Cache.Get<T>(key);
       }

       public static bool Contains(string key)
       {
           try
           {
               return Cache.ContainsKey(key);
           }
           catch
           {
               return false;
           }
       }

       public static bool Invalidate(string key)
       {
           try
           {
               return Cache.Remove(key);
           }
           catch
           {
               return false;
           }
       }

       public static void Expire(string key, DateTime expireAt)
       {
           Cache.ExpireEntryAt(key, expireAt);
       }
   }

Com a nossa classe CacheManager criada, vamos abrir o HomeController e adicionar o código abaixo.

public class HomeController : Controller
{
   public ActionResult Index()
   {
       String message = "Gravando a mensagem no cache redis";

       //Grava no cache
       CacheManager.Set<String>("message", message);

       //Obtem do cache
       String messageFromCache = CacheManager.Get<String>("message");

       ViewBag.Message = messageFromCache;

       return View();
   }
}

Como podem ver na figura abaixo, nosso Cache Redis está funcionando.

Redis4

ASP.NET Session State com Redis

Com nosso Cache Redis funcionando, vamos adicionar mais uma funcionalidade. Ele agora será uma provedor de sessão do ASP.NET e porque isso é interessante ? Porque sempre que utilizamos a sessão do ASP.NET ele irá gravar no Redis, isso mesmo, ao invés de ficar utilizando a memória do nosso servidor vamos utilizar o Redis. Com isso nosso servidor pode somente se preocupar em servir página e fazer os processamentos necessários.

Para criar um ASP.NET Session Custom Provider devemos herdar da classe SessionStateStoreProviderBase, essa classe contém os recursos necessários para que possamos criar nosso Custom Provider Session State.

Crie uma classe chamada RedisSessionState e adicione o código abaixo.

public class RedisSessionState : SessionStateStoreProviderBase
  {
      private SessionStateSection sessionStateConfig = null;

      #region Overrides

      public override void Initialize(string name, NameValueCollection config)
      {
          if (config == null)
              throw new ArgumentNullException("config");

          if (name == null || name.Length == 0)
          {
              name = "RedisSessionStateStore";
          }

          if (String.IsNullOrEmpty(config["description"]))
          {
              config.Remove("description");
              config.Add("description", "Redis Session State Provider");
          }

          base.Initialize(name, config);

          sessionStateConfig = (SessionStateSection)ConfigurationManager.GetSection("system.web/sessionState");

      }

      public override SessionStateStoreData CreateNewStoreData(HttpContext context, int timeout)
      {
          return new SessionStateStoreData(new SessionStateItemCollection(), SessionStateUtility.GetSessionStaticObjects(context), timeout);
      }

      public override void CreateUninitializedItem(HttpContext context, string id, int timeout)
      {
          SessionItem sessionItem = new SessionItem();
          sessionItem.CreatedAt = DateTime.Now.ToUniversalTime();
          sessionItem.LockDate = DateTime.Now.ToUniversalTime();
          sessionItem.LockID = 0;
          sessionItem.Timeout = timeout;
          sessionItem.Locked = false;
          sessionItem.SessionItems = string.Empty;
          sessionItem.Flags = 0;

          CacheManager.Set<SessionItem>(id, sessionItem, DateTime.UtcNow.AddMinutes(timeout));
      }

      public override void EndRequest(HttpContext context)
      {
          this.Dispose();
      }

      public override SessionStateStoreData GetItem(HttpContext context, string id, out bool locked, out TimeSpan lockAge, out object lockId, out SessionStateActions actions)
      {
          return GetSessionStoreItem(true, context, id, out locked, out lockAge, out lockId, out actions);
      }

      public override SessionStateStoreData GetItemExclusive(HttpContext context, string id, out bool locked, out TimeSpan lockAge, out object lockId, out SessionStateActions actions)
      {
          return GetSessionStoreItem(true, context, id, out locked, out lockAge, out lockId, out actions);
      }

      public override void InitializeRequest(HttpContext context)
      {
          //NÃO FAZ NADA
      }

      public override void ReleaseItemExclusive(HttpContext context, string id, object lockId)
      {
          SessionItem currentSessionItem = CacheManager.Get<SessionItem>(id);

          if (currentSessionItem != null && (int?)lockId == currentSessionItem.LockID)
          {
              currentSessionItem.Locked = false;
              CacheManager.Set<SessionItem>(id, currentSessionItem, DateTime.UtcNow.AddMinutes(sessionStateConfig.Timeout.TotalMinutes));
          }

      }

      public override void RemoveItem(HttpContext context, string id, object lockId, SessionStateStoreData item)
      {
          CacheManager.Invalidate(id);
      }

      public override void ResetItemTimeout(HttpContext context, string id)
      {
          CacheManager.Expire(id, DateTime.UtcNow.AddMinutes(sessionStateConfig.Timeout.TotalMinutes));
      }

      public override void SetAndReleaseItemExclusive(HttpContext context, string id, SessionStateStoreData item, object lockId, bool newItem)
      {
          string sessionItems = Serialize((SessionStateItemCollection)item.Items);

          try
          {
              if (newItem)
              {
                  SessionItem sessionItem = new SessionItem();
                  sessionItem.CreatedAt = DateTime.UtcNow;
                  sessionItem.LockDate = DateTime.UtcNow;
                  sessionItem.LockID = 0;
                  sessionItem.Timeout = item.Timeout;
                  sessionItem.Locked = false;
                  sessionItem.SessionItems = sessionItems;
                  sessionItem.Flags = 0;

                  CacheManager.Set<SessionItem>(id, sessionItem, DateTime.UtcNow.AddMinutes(item.Timeout));
              }
              else
              {
                  SessionItem currentSessionItem = CacheManager.Get<SessionItem>(id);
                  if (currentSessionItem != null && currentSessionItem.LockID == (int?)lockId)
                  {
                      currentSessionItem.Locked = false;
                      currentSessionItem.SessionItems = sessionItems;
                      CacheManager.Set<SessionItem>(id, currentSessionItem, DateTime.UtcNow.AddMinutes(item.Timeout));
                  }
              }
          }
          catch (Exception e)
          {
              throw e;
          }

      }

      public override bool SetItemExpireCallback(SessionStateItemExpireCallback expireCallback)
      {
          return true;
      }

      public override void Dispose()
      {

      }

      #endregion

      #region Private Methods

      private string Serialize(SessionStateItemCollection items)
      {
          using (MemoryStream ms = new MemoryStream())
          using (BinaryWriter writer = new BinaryWriter(ms))
          {
              if (items != null)
                  items.Serialize(writer);

              writer.Close();

              return Convert.ToBase64String(ms.ToArray());
          }
      }

      private SessionStateStoreData Deserialize(HttpContext context, string serializedItems, int timeout)
      {
          using (MemoryStream ms = new MemoryStream(Convert.FromBase64String(serializedItems)))
          {
              SessionStateItemCollection sessionItems = new SessionStateItemCollection();

              if (ms.Length > 0)
              {
                  using (BinaryReader reader = new BinaryReader(ms))
                  {
                      sessionItems = SessionStateItemCollection.Deserialize(reader);
                  }
              }

              return new SessionStateStoreData(sessionItems,
                SessionStateUtility.GetSessionStaticObjects(context),
                timeout);
          }
      }

      private SessionStateStoreData GetSessionStoreItem(bool lockRecord, HttpContext context, string id, out bool locked, out TimeSpan lockAge, out object lockId, out SessionStateActions actionFlags)
      {
          SessionStateStoreData item = null;
          lockAge = TimeSpan.Zero;
          lockId = null;
          locked = false;
          actionFlags = 0;

          string serializedItems = "";

          int timeout = 0;

          try
          {
              if (lockRecord)
              {
                  locked = false;
                  SessionItem currentItem = CacheManager.Get<SessionItem>(id);

                  if (currentItem != null)
                  {
                      if (!currentItem.Locked)
                      {
                          currentItem.Locked = true;
                          currentItem.LockDate = DateTime.UtcNow;
                          CacheManager.Set<SessionItem>(id, currentItem, DateTime.UtcNow.AddMinutes(sessionStateConfig.Timeout.TotalMinutes));
                      }
                      else
                      {
                          locked = true;
                      }
                  }
              }

              SessionItem currentSessionItem = CacheManager.Get<SessionItem>(id);

              if (currentSessionItem != null)
              {
                  serializedItems = currentSessionItem.SessionItems;
                  lockId = currentSessionItem.LockID;
                  lockAge = DateTime.UtcNow.Subtract(currentSessionItem.LockDate);
                  actionFlags = (SessionStateActions)currentSessionItem.Flags;
                  timeout = currentSessionItem.Timeout;
              }
              else
              {
                  locked = false;
              }

              if (currentSessionItem != null && !locked)
              {
                  CacheManager.Invalidate(id);

                  lockId = (int?)lockId + 1;
                  currentSessionItem.LockID = lockId != null ? (int)lockId : 0;
                  currentSessionItem.Flags = 0;

                  CacheManager.Set<SessionItem>(id, currentSessionItem, DateTime.UtcNow.AddMinutes(sessionStateConfig.Timeout.TotalMinutes));

                  if (actionFlags == SessionStateActions.InitializeItem)
                      item = CreateNewStoreData(context, 30);
                  else
                      item = Deserialize(context, serializedItems, timeout);
              }
          }

          catch (Exception e)
          {
              throw e;
          }
          return item;
      }
      #endregion

      #region Session Item Class
      public class SessionItem
      {
          public DateTime CreatedAt { get; set; }
          public DateTime LockDate { get; set; }
          public int LockID { get; set; }
          public int Timeout { get; set; }
          public bool Locked { get; set; }
          public string SessionItems { get; set; }
          public int Flags { get; set; }
      }

      #endregion
  }

Agora nosso provedor de Sessão do Redis está configurado. O próximo passo é adicionar no web.config de nossa aplicação a configuração de Custom Session State

Abra o web.config e na sessão system.web adicione a seguinte configuração.

<sessionState timeout="1" mode="Custom" customProvider="RedisSessionState" cookieless="false">
  <providers>
   <clear />
   <add name="RedisSessionState" type="RedisCache.RedisSessionState" />
   </providers>
  </sessionState>

Com o web.config configurado vamos ver o resultado. Vamos alterar o HomeController e adicionar o seguinte código.

public class HomeController : Controller
{
       public ActionResult Index()
       {

           //Gravando na Sessão utilizando Redis
           String messageSession = "Agora Estou utilizando Sessão";

           Session["RedisMessage"] = messageSession;

           //Obtendo o objeto da sessão utilizando Redis
           var messageFromSession = Session["RedisMessage"];

           String message = "Gravando a mensagem no cache redis";

           //Grava no cache
           CacheManager.Set&lt;String&gt;("message", message);

           //Obtem do cache
           String messageFromCache = CacheManager.Get&lt;String&gt;("message");

           ViewBag.Message = messageFromCache;

           return View();
       }

   }

Podemos ver na figura abaixo que o Redis integrado ao ASP.NET MVC, gerenciando as sessões do usuários e também como um servidor de cache.

Redis5

Redis é um banco de dados muito útil quando precisamos utiliza-los como Cache e Session State, ele tem alta performance para acesso ao objetos e uma rápida escrita. Ele é ideal quando temos vários Servidores Web utilizando Load Balance no qual temos que prover uma solução para armazenamento de Sessão e Cache de objetos.

O código deste projeto você pode encontrar no meu GitHub através deste link

Clique aqui e aprenda mais sobre o Redis NoSQL Server

Não deixem de comentar!

Abs e até a próxima

Rafael Cruz

É desenvolvedor .NET há mais de 12 anos, certificado Microsoft desde de 2006, instrutor oficial Microsoft há 5 anos, Pós Graduado em Engenharia de Software pela UFRJ, suas certificações são Microsoft Certified Trainer, Microsoft Certified Solution Developer (Web, Application Lifecycle Management), Microsoft Certified Professional Developer, Microsoft Certified Tecnology Specialist.
Atualmente trabalha como arquiteto de sistema, escreve artigos no .NET Coders e no seu blog rafaelcruz.azurewebsites.net para compartilhar experiências na área de desenvolvimento Web, Aplicativos Móveis e Cloud Solutions.

Twitter LinkedIn 

Did you find apk for android? You can find new Free Android Games and apps.

Comentários

comentarios