Redis ile Azure da Mvc, AngularJs ve SignalR İle PubSub Bölüm2

Selamlar,

Bugünkü makalede, bir önceki yazıda bahsedilen Redis ile Azure’da Mvc, AngularJs ve SignalR ile PubSub konularına devam edilmektedir.

Bir önceki makalenin sonunda ilgili kategoriye ait itemların girişi yapmıştır. Şimdi de, girilen itemlar güncellenecektir.  Aşağıda görüldüğü gibi seçilen kategoriye ait itemlar listelenmektedir. “item”‘a ait “Düzenle” buttonu tıklandığında “EditItem()” function’ına, ilgili parametreleri ile birlikte gidilir.

$scope.EditItem(): Aşağıda görüldüğü gibi “EditItem()” action’ına ilgili “ProductID” ve “ItemID” parametreleri gönderilmiştir.

Yukarıdaki Action’a ilgili Url’den ulaşabilmek için “Route.Config.cs” dosyasına aşağıdaki path’in eklenmesi gerekmektedir.

HomeController.cs/EditItem(): Aşağıda görüldüğü gibi ilgili parametreler “ViewBag” ile ilgili “EditItem” view’ına gönderilmektedir.

ItemEdit

EditItem.cshtml: Aşağıda görüldüğü gibi güncellenecek data bilgileri(“ProductID” ve “ItemID”) “ViewBag”‘ler ile hidden fieldlara yazılmıştır. “Name” ve “Price” alanları “ng-model” directive’i ile “EditItem.Price” ve “EditItem.Name” propertylerine bağlanmışdır. “Kaydet” buttonu tıklanınca, “ng-click” directive’i ile “UpdateItem()” function’ı tetiklenir.

EditItem.js:  Aşağıda görüldüğü gibi sayfa ilk yüklendiği zaman “GetEditItem()” action’ına post işlemi yapılmakta ve hidden alanlara yazılan “Id” ve “ProductID” ilgili action’a parametre olarak post edilmektedir. Dönen result(düzenlenecek item’ın kendisi) AngularJS’deki “$scope.EditItem” property’sine atanmaktadır. “$scope.Update()” function’ında güncellenen item’ın “Price”,”Name”,”ProductID” ve “Id” alanları yaratılan item sınıfı içinde “UpdateItem()” action’ına gönderilmekte ve işlemin tamamlanması ardından ana sayfaya dönülmektedir.

HomeController.cs/GetEditItem(): Aşağıda görüldüğü gibi “Edit” sayfasına gelinince, önce “IRedisTypeClient”‘a bağlanılmış daha sonra da ilgili ProductID’ye göre itemlar “itemClient.Lists[]” ile çekilmiştir. Son olarak düzenlenecek item, linq ile bulunup Json olarak dönülmüştür.

Llen

C# tarafındaki kodların Redis karşılığ yukarıdaki gibidir:

  • “LLEN” “urn:item:3” : Liste’nin toplam eleman sayısını verir.
  • “LRANGE” “urn:item:3” “0” “-1”: Liste içindeki elemanların başlangıç ve bitiş indexleri verilerek toplu olarak listelemesini sağlanır. Burada “-1” sonuna kadar yani tümü anlamına gelmektedir.

HomeController.cs/UpdateItem():  Aşağıda, güncellenecek itemin bilgileri girildikten sonra kaydet buttonuna basılınca post edilen “UpdateItem()” methodu gözükmektedir. Öncelikle item’ın bulunduğu ürün gurubuna göre tüm elemanlar çekilir. Bu bir listeye aktarılır. List içindeki ilgili itemlar arasından “Linq” extension kullanılarak güncellenecek item’in index numarası bulunur. Aslında burada liste içindeki tüm itemlar gezilmiş ve “id” numarasına göre tek tek bakılarak güncellenecek item’ın index’i aranmıştır. “id” değerine göre linq ile çekilen bu item, önce güncellenmiş daha sonra bulunduğu index’e ilgili liste’den çıkarılmışdır. Son olarak fiyata göre güncellenen bu item, Liste’ye tekrar ait olduğu sıraya göre konmuştur.

Not: Görüldüğü gibi Redis’de List yapıları güncellemeye pek te uygun yapılar değildir. Çünkü ilgili item’ı key’e göre değil index numarasına göre bulmak gerekmekte ve bunun için belkide tüm liste dönülmektedir. Bu tür durumlarda “Hashes” gibi yapılar ile çalşmak daha doğru sonuclar doğurabilir. Siz de bu konuda fikirlerinizi paylaşırsanız sevinirim.

Yukarıda ServisStack ile yapılan işlemlerin redis tarafındaki komut karşılıkları aşağıdaki gibidir.

“LSET” “urn:item:3” “0”: Listenin belirlenen indexdeki elemanına değer atanır.

“LREM” “urn:item:3” “0”: Listeden belirlenen sayı kadar eleman çıkarılması sağlanır. Çıkarılacak sayı pozitif ise baştan sona, negatif ise sondan başa doğru belirtilen sayı kadar eleman çıkarılır. “ServiceStack” tarafında karşılığı “itemList.RemoveAt()”‘dir.

“RPUSH” “urn:item:3” : Belirlenen key ile Listeye eleman eklenmesi sağlanır. “ServiceStack” tarafında karşılığı “itemList.Add()”‘dir.

Range2

Seçilne Product’a göre ürünün güncellenmesi on the fly olarak Redis’de rem memoryde yapılmaktadır.

RedisBlog

Şimdi sıra geldi en ucuz 5 ürünü kayar band olarak ekranın altında listelemeye. Bunun için öncelikle yeni bir ürün giriş olduğunda, bu ürünü “SortedSet” dediğimiz sıralı bir diziye de koyalım. Böylece fiyat bilgisine göre sıral ürünler içinden ilk 5’i alınarak en ucuz 5 ürüne ulaşılır.

history

SaveItem(): Yeni bir ürün girişi olduğunda “urn:Rank” keyword’ü ile SıralıSet’e ilgili item “Name” ve “Price” alanları ile atılır.

Yukarıdaki durumun Redis’deki karşılığı aşağıdaki gibidir:

“ZADD” “urn:Rank” “100”: SortedList’e yeni girişi yapılan ürün fiyatı ile birlikte eklenir. (AddItemToSortedSet()).

AddRange3

Aynı şekilde var olan bir ürünün güncellenmesinden sonra “urn:Rank” keyword’unden güncellenen item çıkarılır ve tekrar eklenir. Burada amaç ilgili “Price”‘a göre güncellenen item’ın tekrardan “SortedSet”‘e ait olduğu sıraya göre konmasıdır. Böylece son güncel sıra “SortedList”‘de de korunmuş olunur.

Yukarıdaki durumun Redis’deki karşılığı aşağıdaki gibidir:

Update

“ZREM” “urn:Rank” : Sıralı listeden belirlenen key’e göre ilgili item çıkarılır. (RemoveItemFromSortedSet())

“ZADD” “urn:Rank” : Sıralı listeye fiyatı değişen ürün adı ve fiyatı ile birlikte tekrar eklenir.(AddItemToSortedSet)

Şimdi sıra geldi Index.cshtml sayfa ilk yüklenirken ilgili Rank List’in AngularJS tarafından doldurulmasına:

index.js: Aşağıda görüldüğü gibi “$scope.Ranks” propertysi “GetRanks()” methodu ile aşağıdaki gibi doldurulur.

HomeController.cs/GetRanks():  Redis’e bağlanıldıktan sonra aşağıda görüldüğü gibi sıralı listeden ilk 4 item “GetRangeWithScoresFromSortedSet()” methodu ile çekilir. Daha sonra ilgili list, item listesinde gezilerek “Id=counter” şekilinde bir identity değeri de eklenerek atanır. Sonra ilgili “RankList“, Json olarak geri dönülür.

Index.cshtml: İlgili en ucuz 5 ürün aşağıda görüldüğü gibi, AngularJS ile “Ranks” içinde gezilerek ekrana basılır.

Rank

Şimdi sıra geldi makalenin en can alıcı ve önemli kısmına. Değişen bu “SortedList” datalarının, tüm clientlar’a real time olarak bildirilmesine. Bu işlemin redis karşılığı “Pub/Sub” olarak bilinen [Publisher / Subscriber]’dır. Yani herhangi bir ürün girişinin ya da güncellemesinin olması durumunda, yeni oluşan en ucuz son 5 ürün sıralı listesi o anki tüm clientlara real time olarak bildirilmelidir. Bu aslında Observer Design Pattern ‘dan başka birşey değildir.

500px-Observer.svg

Öncelikle yeni bir ürün girişi olduğu zaman, oluşan yeni “SortedList“‘i tüm clientlar ile paylaşalım. Projeye “JsonConvert” dönüşümü için aşağıdaki “Newtonsoft.Json” package’ı indirilir.

Newton

HomeController.cs/SaveItem(): Methodunun sonuna aşağıdaki kodlar eklenir. Burada amaç, yeni bir ürün girişi olduğu zaman var olan SortedList yeniden oluşturulur. Böylece “GetRangeWithScoresFromSortedSet()” ile en ucuz ilk 5 ürün alınmış olunur. Daha sonra “RankList” adında bir “List<Item>“‘a doldurulur.  İlgili “RankList“, json formatına Newtonsoft kütüpahanesi kullanılarak “JsonConvert.SerializeObject()” methodu ile çevrilir. Son olarak json formatına çevirilen “itemJson“, “client.PublishMessage()” komutu ile “Ranksubscriber’ına gönderilir.

Yukarıdaki kodların Redis tarafındaki karşılığı aşağıdaki gibidir:

NewPub

“ZRANGE” “urn:Rank” “0” “4” “WITHSCORES”: Sıralı listeden yani SortedList’den ilk 5 tanesi value değerleri ile birlikte çekilir.(GetRangeWithScoresFromSortedSet()).

“PUBLISH” “Rank” : “Rank” Subscriber’ına  Json olarak çevrilen tüm “RankList” gönderilir.(PublishMessage()).

HomeController.cs/UpdateItem(): Methodunun sonuna aşağıdaki kodlar eklenir. Var olan bir ürün güncellendiğinde, “SortedList” değiştiği için önce ilk 5 kayıt çekilir, daha sonra aynı yeni ürün girişinde olduğu gibi, “RankList“‘e konur ve json formatına çevrilip “Rank” subscriber’ına gönderilir.

Şimdi sıra geldi, bu gönderilen mesajı karşılıyan “Rank” Subscriber’ını.  Bu işlem için yeni bir consol application yapıcağız. Aslında herbir yeni “Subscriber” için farklı bir console application yapılabilir.

Ve böylece yoğun olabilecek bir iş topluluğunu, parça parça mikroservices olarak adlandırabileceğimiz yapılar ile kolaylıkla bölebilir; ilgili mikroservices üzerinde bir takım değişiklikler yaparak bir docker içine koyup scale edip performansı çok daha fazla arttırabiliriz.

Rank/Program.cs(Console Application): Aşağıda görüldüğü gibi “CreateSubscription()” methodu ile “sub” adında bir subscription oluşturulmuştur. “OnMessage()” methodunda publisher dinlenerek, herhangi bir mesaj geldiğinde şu an için ilgili mesajNewtonsoft” kütüpahanesi kullanılarak “json” formatına  çevrilip bir item liste atanmıştır. Yaratılmış olan subscribe’ın ait olmuş olduğu channel “SubscribeToChannels()” methodu ile “Rank“‘e atanmıştır. Listeye atılan “item” modeli aşağıda görüldüğü gibi “item” sınıfı ile tanımlamıştır.

Şimdi sıra geldi Console Application’da alınan bu mesajları”SignalR” kullanarak o anki clientlara göndermeye. Öncelikle aşağıdaki SignalR paketleri Nuget’den indirilir.

signalR

Öncelikle “Rank” adında signalR “Hub” sınıfı oluşturup, oluşan yeni “Rank List” tüm clientlara real time olarak “getRank()” function’ı ile gönderilir.

1-)HomeController.cs/Rank:

2-) Index.js: Aşağıda görüldüğü gibi ilgili “Rank” signalR class’ına “$.connection.rank” ile bağlanmıştır. Ve daha sonra ilgili hub başlatılmıştır.”hub.start()“. “getRank()” function’ı kendisine gelen “rankList“‘i  “$scope.Ranks”‘e atayıp “$scope.$apply()” ile bind etmiştir. Böylece sayfanın sonundaki kayan bandın real time olarak tüm clientlar için değişmesi sağlanmıştır.

screen

3-) Rank/Program.cs: Öncelikle aşağıda görüldüğü gibi,  herhangi bir ürün girişinden veya güncellenmesinden sonra “Redis”‘in “Publisher” özelliği ile [“client.PublishMessage(“Rank”, itemJson);“] değişen SortedList önceden yazılan Micro Servis olarak tabir ettiğimiz “Program.cs ConsoleApplication”‘daki “Rank Subscribe”‘ına gönderilir. “OnMessage()” methodunda, yakalanan message yani “RankeList”, yazılmış olan “SignalRClass”‘ındaki “SendRank()” asenkron methodunu call eder. İlgili method, “http://localhost:34511/”‘da tanımlı “Rank” signalR sınıfına bağlanıp tüm clinetlara güncel son sıralı listeyi göndermek için “SendRank()” methodunu tetikler.

Böylece değişen ve yeni girilen tüm data, asenkron olarak başka bir microservices(Consol Application) tarafından Redis’in “pub/sub” özelliği kullanılarak Json formatında yakalanması; yakalanan sortedListin, SignalR teknolojisi kullanılarak tüm clientlara real time olarak gönderilmesi, ve en sonunda Clientlara gelen datanın, AngularJs modeli doldurularak ekrana real time olarak basılması sağlanmıştır.

Yukarıda görüldüğü gibi 4 farklı teknoloji Mvc, AngularJS, Azure üzerinde Redis ve SignalR birlikte çok uyumlu bir şekilde çalışmaktadır. Azure üzerinde oluşturulan NoSql memcached Redis, kayıtları çok hızlı bir şekilde yazmakta ve okumaktadır. Böylece girilen data işlemlerinde (güncelleme, listeleme ve sıralama gibi), Redis kullanmaktadır. İlgili dataların ekrana basılması işi AngularJs’e aittir. Böylece modellerde olan değişikliklerin, “UI” tarafında ayrıca kod yazılmadan gösterilmesi sağlanmıştır. Yine Redis’in “Pub/Sub” özelliği ile değişen veya yeni girilen tüm datalar,  kendi içinde socket teknolojisi kullanılarak farklı bir Consol Application’a gönderilmiştir. Burada amaç, performans ve uygulamadan bağımsız arkadan çalışan bir yapı oluşturmaktır. Ayrıca herbir tablo için farklı bir consol application ya da diğer bir düşünce ile mikroservices yaratılarak performans iyice arttırılabilir. Hatta bu mikroservices ler docker yapısına uygun bir hale getirilerek, docker içine konup Azure ortamında scale yapılarak büyük trafikler için performanslı yapılar haline getirilebilir. Daha da ileri gidersek, bu Console Applicationlarda, gelen tüm Json Data database ya da loglama amaçlı DocumentDB’ye de yazılabilir.

Görüldüğü gibi hayal etmenin sonu ve sınırı yoktur. Kodları iyileştirmenin, bir adım daha ileriye götürmenin tek yolu, araştırmak ve yenilikleri takip etmekten başka birşey değildir. Yeni bir makalede görüşmek üzere hoşçakalın.

Source Code: https://github.com/borakasmer/RedisPubSubWithSignalR

Herkes Görsün:

Bunlar da hoşunuza gidebilir...

6 Cevaplar

  1. Yigit dedi ki:

    Hocam 2. bolum dediginiz kadar varmis elinize saglik.
    Fakat redis agirlikli daha enterprise uygulamalar bekliyoruz :)

    Saygilar,

    • borsoft dedi ki:

      Teşekkürler Yiğit;
      Önceden planladığım birkaç konu daha var. Onlardan sonra redis’i daha farklı projeler ile irdelemeye devam edebiliriz.

  2. Mehmet dedi ki:

    Hocam merhabalar, redis ile anlattığınız gibi azuredan değil de uzakmasaüstü bağlantısından -tarih/saat vb.- veri çekmemiz mümkün müdür ?

    • borsoft dedi ki:

      Selam Mehmet,

      Redis ile Azure yerine remote bir makinadan data çekmen tabiki mümkün…

      Kolay gelsin…

  3. Berdan dedi ki:

    Merhaba,

    Nuget package manager üzerinde iki farklı redis package’i mevcut: StackExchange ve ServiceStack.
    Sizin ServiceStack seçmenizde özel bir neden var mı?

    • borsoft dedi ki:

      Evet var. StackExchange daha çok Azure gibi Cloude üzerinden redis kullanılıyor ise tercih edilen bir Library diyebiliriz.
      Ben daha çok model yapısı üzerinde çalıştığım için uygulama cloude’da çalışmasına rağmen ServisStack kütüpahanesini kullandım. Türkiyede iş hayatında da genelde ServisStack kullanılır. Hatta ben hiç StackExchange kullanan bir yapı ile karşılaşmadım:)

      İyi çalışmalar.

Bir Cevap Yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir