.Net Core’da Redis Cache Kullanımı
Selamlar Arkadaşlar,
Bu makalede macOS High Sierra’da öncelikle nasıl Redis kurulucağını inceleyip, daha sonra bir .Net Core Mvc proje üzerinde kullanımını hep beraber inceleyeceğiz. Redis yani distributed cache’de amaç, bir web sayfasına her gelinildiğinde servise ya da DB’ye gitmektense belirlenen bir süre içerisinde In Memory olarak yani rem’den istenen datanın çekilerek performans artışının sağlanmasıdır.
Öncelikle eğer makinanızda HomeBrew yok ise kurmanızı tavsiye ederim. Homebrew kısaca Mac OS X işletim sistemi üzerinde yazılım kurulumunu kolaylaştıran bir paket yönetim sistemidir. Bash command satırı açılıp, aşağıdaki komut çalıştırılarak ilgili kurulum kolaylıkla yapılabilir.
1 |
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" |
Artık Redis’in kurulması için yapılması gereken tek şey, aşağıdaki komutun çalıştırılmasıdır.
1 |
brew install redis |
Bundan sonra bash üzerinde, aşağıdaki komut yazılarak redisin “6379” portundan ayağa kalkması sağlanır.
1 |
redis-server |
Ayrıca yeni bir bash command açıldığı taktirde, aşağıdaki komut satırı yazılarak yeni bir redis client oluşturulabilir.
1 |
redis-cli |
Eğer açılan redis client’dan aşağıdaki gibi “ping” komutu yazıldığında, “PONG” cevabı alınıyor ise herşey yolunda demektir.
Şimdi gelin .Net Core MVC projemizi “dotnet new mvc -o redisCore” şeklinde oluşturalım.
.Net Core Mvc projesinde Redis için ServiceStack, kullanılabilecek apilerden biridir. İlgili kütüphane aşağıdaki komut satırı ile projeye eklenir.
1 |
dotnet add package ServiceStack.Redis.Core |
Startup.cs/ConfigureServices() : Redisin .Net core projesinde kullanımı için Startup.cs ‘de AddDistributedRedisCache() methodu ile redis-server’ın IP ve Portu tanımlanıp son olarak root adı belirlenir.
1 2 3 4 5 6 7 8 9 |
public void ConfigureServices(IServiceCollection services) { services.AddMvc(); services.AddDistributedRedisCache(option=>{ option.Configuration="127.0.0.1:6379"; option.InstanceName="master"; }); } |
HomeController/HomeController() : Aşağıda görüldüğü gibi Dependency Injection ile Constructer’da “IDistributedCache” nesnesi local bir değişkene atanır.
1 2 3 4 5 6 |
private readonly IDistributedCache _distributedCache; public HomeController(IDistributedCache distributedCache) { _distributedCache = distributedCache; } |
Index.cshtml: Bu uygulamada örnek amaçlı olarak sayfaya önceden redis console’dan atanmış bir isim ve 5 saniyede bir değişecek anlık zaman bilgisi ekrana basılıcaktır. Sayfa sürekli refresh yapıldığı anda sayfa üzerindeki zaman bilgisi 5er sn aralıklar ile değişecek, geri kalan zaman ve isim bilgisi redis üzerinden çekilecektir. Kısaca redis timeout süresi 5sn’dir. Redis ile alakalı yapılan tüm işler, aşağıda görüldüğü gibi asenkron olarak gerçekleştirilmiştir.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public async Task<IActionResult> Index() { var cacheKey = "Time"; var existingTime = _distributedCache.GetString(cacheKey); if (string.IsNullOrEmpty(existingTime)) { existingTime = DateTime.UtcNow.ToString(); var option = new DistributedCacheEntryOptions().SetSlidingExpiration(TimeSpan.FromSeconds(5)); option.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(5); string name = await _distributedCache.GetStringAsync("Name"); await _distributedCache.SetStringAsync(cacheKey, $"{name}: {existingTime}", option); } ViewBag.Time = await _distributedCache.GetStringAsync(cacheKey); return View(); } |
- Redis’de saklanacak cache ismine “Time” verilmiştir.
- “var existingTime = _distributedCache.GetString(cacheKey)” : Daha önceden ilgili key’e ait bir kayıt var mı diye bakılmıştır.
- “if (string.IsNullOrEmpty(existingTime))” : Eğer herhangi bir kayıt yok ise koşulu tanımlanmıştır.
- “existingTime = DateTime.UtcNow.ToString()” : Anlık zaman bilgisi alınmıştır.
- “var option = new DistributedCacheEntryOptions().SetSlidingExpiration(TimeSpan.FromSeconds(10))” : Burada SetSlidingExpiration() anlamı, Redis’de ilgili key ile belli bir zaman mesela bu örnekte 5sn içinde hiçbir işlem yapılmaz ise, cache’in düşeceğinin belirlenmesidir.
- “option.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(5)” : Burada AbsoluteExpirationRelativeToNow() anlamı, Redis’de ilgili keye ait cache’in mutlaka yani işlem yapılsın ya da yapılmasın 5sn sonunda düşeceğinin belirlenmesidir.
- “string name = await _distributedCache.GetStringAsync(“Name”) :” Redis’de bash command satırından daha önceden tanımlanan isim, burada çekilmiştir.
Önemli Not: ServiceStackCore’da string data Redisde nedense string olarak değil de hash olarak tutulmaktadır. Bunun nedenini henüz ben de anlamış değilim. Ama geliştiricilerin mutlaka bir bildiği vardır diyerek, bu konuyu onlara sordum. Cevabı hep beraber göreceğiz. Yukarıdaki resimde görüldüğü gibi “Time” keyinin başına instance adı da eklenip key oluşturulduğunu gördüm. Yani ==>”master” + “Time” şeklindeki key’,in “type masterTime” şeklinde bir komut ile tipi araştırıldığında “hash” ile karşılaşılmaktadır.
- “HSET masterName data BoraKASMER” : Console tarafında “Name” değişkenine hash formatında data, solda görüldüğü gibi atanmıştır.
- “await _distributedCache.SetStringAsync(cacheKey, $”{name}: {existingTime}”, option)” : SetStringAsync() methodu ile ilgili “masterTime” key’ine “BoraKASMER”+ existingTime’dan oluşan string 5 sn süre ile asenkron olarak atanmıştır.
- “ViewBag.Time = await _distributedCache.GetStringAsync(cacheKey)“: Redis cache’den çekilen data “Index.cshtml“‘de sayfaya basılmak amacı ile “ViewBag.Time“‘a atanır.
Önemli Not 2: SetStringAsync() ve GetStringAsync() methodları aslında işimizi büyük ölçüde kısaltmaktadır. Aşağıda bunun yerine, “SetAsync() ve GetAsync()” methodları kullanılması durumunda yapılması gereken Encoding() işlemleri gösterilmiştir. İşte tüm bu işlemleri “SetStringAsync() ve GetStringAsync()” methodları bizim için yapmaktadır.
1 2 3 4 5 6 7 8 |
const string key = "message"; const string message = "hello"; var data = Encoding.UTF8.GetBytes(message); await _distributedCache.SetAsync(key, data); var cachedData = await _distributedCache.GetAsync(key); var cachedMessage = Encoding.UTF8.GetString(cachedData); |
Index.cshtml: İlgili ViewBag’in ekrana basılması.
1 2 3 |
<div class="container"> <h1>@ViewBag.Time</h1> </div> |
Şimdi gelin bir de herhangi bir class(sınıf)’ı redis de nasıl tutabiliriz onu incleyelim.
Person.cs: Aşağıdaki gibi bir sınıfımız olsun
1 2 3 4 5 6 |
public class Person{ public string Name { get; set; } public string Surname { get; set; } public int Age { get; set; } public bool isMail { get; set; } } |
HomeController/HomeController(): Aşağıdaki constructerda görüldüğü gibi person sınıfı tipinde yeni bir kayıt oluşturulmuştur. Redis cache’e 2 farklı yol ile oluşturulan bu person kaydı atılmıştır.
- “var data = JsonConvert.SerializeObject(person);” : Newtonsoft ile Person sınıfı Json string bir sınıfa dönüştürülmüştür.
- “var dataByte = Encoding.UTF8.GetBytes(data);”: String değişken byte[] Array’e dönüştürülmüştür.
- “_distributedCache.Set(“Person”,dataByte);” : Redis’de “Person” key’ine karşılık value değeri olarak oluşturulan bu dataByte [] dizisi Set() methodu ile atılmıştır.
- “_distributedCache.SetString(“Person2″,data);” : Newtonsoft ile en başta Json string’e dönüştürülen data Redis’e, SetString() methodu ile byte[] dizisine çevrilmeden doğrudan atılması sağlanmıştır. Bu da başta bahsedilen Set() Vs SetString() methodları farkına güzel bir örnek olmuştur.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public HomeController(IDistributedCache distributedCache) { _distributedCache = distributedCache; Person person=new Person(); person.Name="Arya"; person.Surname="Kaşmer"; person.Age=0; person.isMail=false; var data = JsonConvert.SerializeObject(person); var dataByte = Encoding.UTF8.GetBytes(data); _distributedCache.Set("Person",dataByte); _distributedCache.SetString("Person2",data); } |
HomeController/Index: Aşağıda görüldüğü gibi Redis’e 2 farklı Method “Set()” ve “SetString()” ile atılan person sınıfı “Get()” ve “GetString()” methodları ile çekilerek Index sayfasına view’a model olarak atanmıştır.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
public async Task<IActionResult> Index() { var cacheKey = "Time"; var existingTime = _distributedCache.GetString(cacheKey); if (string.IsNullOrEmpty(existingTime)) { existingTime = DateTime.UtcNow.ToString(); var option = new DistributedCacheEntryOptions().SetSlidingExpiration(TimeSpan.FromSeconds(5)); option.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(5); string name = await _distributedCache.GetStringAsync("Name"); await _distributedCache.SetStringAsync(cacheKey, $"{name}: {existingTime}", option); } ViewBag.Time = await _distributedCache.GetStringAsync(cacheKey); /* //Person var personByte=await _distributedCache.GetAsync("Person"); var personString=Encoding.UTF8.GetString(personByte); var person=JsonConvert.DeserializeObject<Person>(personString); return View(person); */ //Person2 var personString=await _distributedCache.GetStringAsync("Person"); var person=JsonConvert.DeserializeObject<Person>(personString); return View(person); } |
Index Page
Index.cshtm: Aşağıda görüldüğü gibi view model olarak gönderilen “Person“, ekrana yukarıda görüldüğü gibi basılmaktadır.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
@model Person <div class="container"> <h1>@ViewBag.Time</h1> </div> <h3><b>Person:</b></h3> <table class="table"> <tr> <td>Ad:</td> <td>@Model.Name</td> </tr> <tr> <td>Soyad:</td> <td>@Model.Surname</td> </tr> <tr> <td>Yaş:</td> <td>@Model.Age</td> </tr> <tr> <td>Cinsiyet:</td> <td> <input type="checkbox" name="gender" id="gender" checked="@Model.isMail">Erkek <input type="checkbox" name="gender" id="gender" checked="!@Model.isMail">Kız </td> </tr> </table> |
Geldik bir makalenin daha sonuna. Bu makalede Redis’in MacOS X bir işletim sistemine nasıl kurulduğunu ve .Net Core Mvc bir projede nasıl implemente edildiğini hep beraber inceledik. Redis’e öncelikle bir değişkeni süre bazlı hangi parametrelere göre atılabileceğini inceledikten sonra, aynı şekilde bir sınıfı NewtonSoft kullanarak Set() ve SetString() methodlarını kullanarak 2 farklı yol ile cache’e atadık. Daha sonra gönderilen bu view model’i Redis Cache’den NewtonSoft ile alıp ekrana Razor ile bastık.
Yeni bir makalede görüşmek üzere hoşçakalın.
Source:
Küçük bir hatayı belirtmek istiyorum
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddDistributedRedisCache(option=>{
option.Configuration=”127.0.01:6379″;
option.InstanceName=”master”;
});
}
olarak verdiğiniz koddaki:
option.Configuration=”127.0.01:6379″;
bölümünde loopback adresinde nokta unutulmuş, olması gereken:
option.Configuration=”127.0.0.1:6379″;
Teşekkür ederim.
Düzeltildi…
Ayrıca, bu yararlı paylaşımınız için Teşekkürler.
Makale için çok teşekkürler. Redis’ i doğrudan session yönetimine entegre olarakta kullanabiliyoruz. En güzel avantajı ise, eğer ki projenizde session kullandıysanız ve Redis’e geçmek istiyorsanız, inanılmaz derecede basit olması. Yani entegre Session[“data”] = “veri”; şeklinde session kullandıysanız, hiç bozmanıza gerek kalmıyor. Redis i cache ve session da kullanmak çok güzel.
Merhabalar,
jwt kullanmak mı yoksa redis ile session tutmak mı ?
Merhaba, bana mail gelmemiş o nedenle sorunuzu göremedim. Şans eseri şimdi denk geldim. Siz sorunuzun cevabını çoktan bulmuşsunuzdur ancak, yine de cevaplayayım. Projenize göre değişir. Jwt özellikle Token Based olduğu için Web Servis mimarisinde kullanımı daha yaygındır. Sürekli her Web İsteğinde, Header da Token göndermeniz gerekiyor. Bununla uğraşırım diyorsanız tabiki Jwt kullanabilirsiniz. Ancak kullanıcı logout olmadan tarayıcıyı kapatıp geri açtığında bir istek alırsa sistem, jwt artık header da gönderilemeyeceği için (tarayıcı cache kullanmayacağınız için) login ekranına düşecektir. Jwt kullanmadım, teorik bilgilerim ile yardımcı olmaya çalıştım, sürçü lisan ettiysem affola. Saygılarımla.
oluşturulan keylerin bellekten düşmeden hemen önce veri tabanına kaydedilmesi için nasıl bir yol izlemeliyim?
Senaryonu anlatırsan Hakan daha çok yardımcı olabilirim.
makaleler için görüntülenme sayısını sql serverda tutuyorum. yapmak istediğim görüntülenme sayılarını redis üzerinde tutup görüntüleme oldugunda incr ile arttırmak ve herhangi bir saatte rediste tutulan görüntülenme sayılarını sql servera aktarmak
Gökhan bey merhabalar. Güzel bir makale olmuş elinize sağlık. Sİzin makaleyi okuduktan sonra redis kullanmaya başlamıştım. ama baktım ki ServiceStackCore’da ücretliymiş. Ücretli olup olmadığını kontrol etmedim. önereceğiniz .net core ile çalışan ücretsiz bir sdk var mı.
Tekrardan teşekür ederim.
Selamlar,
Adım Bora :) Stack Exchange kullanabilirsiniz..
Gökhan Bey Merhaba, anlatım için teşekkürler. Emeğinize sağlık. Bir sorum olacak. Person sınıfındaki veriyi redise yazdıktan sonra sadece bir yeri güncellemek istiyorum.(Örneğin person.Age) bunu nasıl yapabilirim ? Teşekkürler.
Merhaba Bora Bey,
Redis’e yeni başlayan biri olarak 2 sorum olacaktı:
1- Redis’i HomeBrew ile Mac OS X işletim sistemi üzerinde yazılım kurulumunu göstermişsiniz. Ücretsiz Windows için kurulumunu nereden yapmasını tavsiye edersiniz.
2-2021 yılı itibariyle güncel paket olarak yine ServiceStack.Redis; mi kullanmalıyız? Microsoft.Extensions.Caching.StackExchangeRedis paketi de kullanılabilir mi?