SignalR’ı Derinlemesine İnceleme

Tüm icatlar ihtiyaçtan doğar. Internet dünya’sının kanayan yarası olan Real Time uygulamalar ya çok resource harcıyordu (clientların belli zaman aralıkları ile server’a ajax post ile request atması(Ajax long polling) ki düşünün 100000 user’ın 10sn’de bir request attığını :) ) yada belli zaman aralıkları ile aynı online gazetelerde olduğu gibi sayfanın yenilenmesi gerekiyordu. Yine bir başka örnek de LES sınavının online olduğunu düşünün; herkes aynı zamanda başlıyacak ve bitirecek. İşte SignalR bu gibi sorunlar için gerçek zamanlı (Real Time) çözüm sunan bir framework’tür.

Yaptığı iş Abstraction Over Transport’ tur. Yani client’daki javascript’i server side’dan çağırmak yada diğer adı ile RPC.Mesela alttaki örnekte kalsik bir chat uygulaması görmekteyiz. Kodları şimdilik çok da dikkate almadan önce yeşil ile belirtilen kısmda client’da click event’inden sonra serverdaki SendMessage() çağrılmıştır ki bu bilmediğimiz birşey değil.Yani klasik ajax post. Ama sonra işler karışıyor:)Serverdaki SendMessage() methodu da  tüm clientlar’daki addMessage() function’ını çağrıyor. İşte bütün mesele budur.

chat02

Bu işlem için eğer browser izin veriyorsa yani HTML5 destekliyorsa WebSocket kullanır. Eğer desteklemiyor ise aşşağıdaki durumlarla karşılaşılır:

  • Internet Explorer 8 veya daha öncesi is Long Polling yapılır.
  • Connection sırasında jsonp Parametresi true olarak atanmış ise gene Long Pooling yapılır.
  • Cross Domain’de SignalR endpoint ile host edilen sayfanın domain’i aynı değil ise:

Server, Client Websocket’i desteklemez ise ve client CORS (Cross-Origin Resource Sharing) desteklemez ise Long Polling yapılır.

  • Eğer client ve server Websocket’i  desteklemez ise Server Sent Events desteklenir ise kullanılır.
  • Server Sent Event  desteklenmez ise Forever Frame denenir.
  • Forever Frame’de olmaz ise en son çare Long Polling yapılır.

what_is_signalr_invocation2

Artık öreneğimize geçebiliriz:

Bir borsa sitesi düşünün client tarafında kağıt değerleri izlenecek. Server tarafında ise kağdın değeri değişitirilince sonuç tüm clientlara pushlanacak.

Önce Modelimizi aşşağıdaki gibi oluşturalım.

HomeController’de dummy data önceden oluşturulmamış ise cache atılarak datayı aşşağıdaki gibi view’a gönderelim.

Şimdi Index.chtml’imizi yani View’ımızı aşşağıdaki gibi kodluyalım.

Aşşağıdaki gibi bir ekranı çıktısı almış olduk:

screen

Şimdi bir de Admin ekranı, yani datanın değiştirileceği kısmı aşşağıdaki gibi kodluyalım.

HomeController:

Cms.cshtml:

 

 

Admin ekran çıktısı aşşağıda görüldüğü gibidir.

cms

Şimdi değişiklik yapıcağımız satırdaki Güncelle butonuna basınca server sidedaki methodumuzu tetikleyeceğiz.

Öncelikle sayfamıza gerekli scriptleri aşşağıdaki gibi koyuyoruz.

Dikkat ederseniz alttaki gibi bir script var.Aslında böyle bir script yok. Çalışma anında signalR tarafından oluşturuluyor.Buna magic script diyorlar:)

Bunun üzerinde daha sonra gene konuşacağız.

<script src=”@Url.Content(“~/signalr/hubs”)”>

Şimdi Cms.chtml’imizde Client tarafındaki DataModelimizi aşşağıdaki gibi oluşturalım. Bu server side’a push edeceğimiz datayı tutacak:

Şimdi de Güncelle butonuna basılınca client side tarafında çağrılan upData() functionunu aşşağıdaki gibi kodluyalım:

Buraya kadar değişiklik yapılan datayı, ilgili model’e doldurduk.Şimdi asıl işi yapacağımız kısma geldik. Tam bu noktada pause basıp öncelikle control tarafını(Server)’ı yazmak istiyorum.

İlk başta signalR’ın çalışması için Startup.cs’e alttaki configuration’ı yazalım.

Şimdi HomeControl kısmına alttaki kodu yazalım:

Yukarıda görüldüğü gibi Microsoft.AspNet.SignalR.Hub sınıfından türetilmiş bir Borsa sınıfımız var. public class Borsa : Hub

Bir de UpdateData() adlı bir methodumuz var.İşte bu method Client Side tarafından Güncelle butonuna basılnca çağrılacak olan methoddur.

Cache’den isme göre var olan datayı çekip gönderilen değiştirilmiş data ile güncelleniyor.Ama hepsinden önemlisi sonunda client taraflı Index.cshtml’deki addData() function’ı çağrılıyor.

Clients.All.addData(data);

Şimdi Cms.chtml’e geri dönelim ve değişen datanın server side’a HomeController’a atılmasını UpdateData(DataModel data)‘nın çağrılmasını yazalım.

Öncelikle hub objectimizi oluşturalım:

Javascripteki borsa bizim HomeController’daki Hub’dan türeyen clasımız. public class Borsa:Hub

$.connection.hub.start() ile server ile bağlantı başlar.Detayına ilerde giricez.

upData() function’ı nı şu şekilde güncelledik:

 messagehub.server.updateData(Data);

server taraflı çalışacak UpdateData(Data) methodu çağrılır.Yani serverdaki demin yazdığımız UpdateData(DataModel data) methoduna karşılık gelmektedir.

Şimdi size ÇOK ÖNEMLİ bir not.Dikkat ettiyseniz serverdaki method ismi ile client’dan çağrılan isimlerde büyük küçük harf  farklılıkları var.Bunu nasıl belirliyicez?

Server side tarafında Hub’classı içinde kullandığımız methodların client side tarafındaki karşılıklarını dinamik oluşan

<script src=”@Url.Content(“~/signalr/hubs”)”></script>

içinden arıyarak orda nasıl oluşturuldu ise client’a aynı isimle koymamız gerekmektedir. Örnek ekran görüntüsü aşşağıdadır.

hub2

Şimdi Index.cshtml’deki addData() function’ını aşşağıda görüldüğü gibi yazalım.

Dikkat edecek olursanız  messagehub.client.addData = function (data) { şeklinde client’da çalışacak addData() function’ı tanımlanır.

Script’in amacı, değişen satırın ufak bir animasyon ile gizleyip yeni datalar ile tekrar gösterilmesidir.

cms

Bunun yanında signalR’ın biraz pekişmesi için cms ekranına aşşağıdaki örnekte olduğu gibi bir dropdownlist koyalım.Seçtiğimiz resme göre index ekranının background’ı o anda açık olan tüm clientlar için değişir.

Cms:

Ve dropdown değiştiğinde clientlarda tetiklenecek changeBG() functionını yazalım:

Index.cshtml’deki changeBG() function’ı aşşağıda görüldüğü gibidir:

Böylece  Cms’ekranında combo’da change eventi tetiklenince tüm clientların arka resmi aşşağıdaki ekran çıktısında görüldüğü gibi değişir.

Untitled

İlerde serverlarımızı scale yaptığımızda yani çoklattığımızda ilgili serverlara bağlı tüm clientlara nasıl erişebileceğimize bakıcaz.Mesela 10000 kişinin 5 server’a dağılmış bir chat uygulamasında yazıştığını düşünün.

1.serverdaki bir client yazı yazdığı zaman 5.serverdaki client bundan nasıl haberdar olucak.İşte bu kurtu içinize düşürdükten sonra makaleme son verebilirim:)

Yeni bir makalede görüşmek üzere herkese hoşçakalın.

Not:Örnek Url: http://borsasignalr.azurewebsites.net/  ve  http://borsasignalr.azurewebsites.net/cms

Herkes Görsün:

Sevebilirsin...

14 Yanıt

  1. Romairo diyor ki:

    SignalR , Microsoft’un realtime altyapısıymış. Başlangıçta ASP.Net ile kullanılan.. gibi bir giriş olmadığı ve sonradan ASP.Net kodları ile birlikte görmeye başlayınca bu sonucu kendim çıkardım :)
    Sonuçta MS’in realtime altyapısız olması düşünülemez, MS’deki geliştiriciler bunu en iyi şekilde geliştirmiş. Ancak realtime web alanında node.js ve son 1 yılda yaygınlaşan Go da çok kullanışlı. MS zaten bazı kodlarında node.js’i de kullanıyor.

  2. borsoft diyor ki:

    Microsoft VisualStudio’da signalR’ı kullanıyor doğrudur.Mesela view’da,css veya js’de değişikliği gidildiği zaman o an açık olan tüm clientlardaki o sayfa refresh olabiliyor.
    Gelecek yazım node.js’in yakın akrabası typescript üzerine olucak.O zaman belki bazı konularda size ışık tutabilirim.
    İyi günler

  3. Uğur diyor ki:

    Hocam Merhabalar,

    Yazılarınız gerçekten çok iyi. Bu kadar uğraşıp emek verdiğiniz için teşekkür ederim.

    SignalR ile yeni tanıştım. Bir şeyler öğrenmeye çalışıyorum. Bir soru var kafamda cevaplarsanız sevinirim.

    SignalR serverdan clientlara push edilme olayı anladığım kadarıyla. Bu örnekte olduğu gibi bütün client’lar etkileleniyor . Peki sadece belli kullanıcıların etkilenmesini istersek ne yapmamız gerekir. Aklıma bir yol geldi ama iyi bir çözüm olduğunu sanmıyorum. Şöyle ki ;

    Server bütün clientlara push etmeye devam edecek fakat client tarafında session bazlı bir kontrolümüz olacak. Yani sadece ilgili kullanıcılar değişikliği görebilecek. Ama aslında tüm clientlara da o bilgi gitmiş olacak. Bazılarında görünecek bazılarında görünmeyecek.Böyle bir yol izlenebilir mi? Yada bunun en doğru yolu nedir?

    • borsoft diyor ki:

      Selam Uğur;
      Öncelikle güzel yorumların için teşekkürler.
      Belli kullanıcılara trigger işlemi için düşüncen özellikle performans anlamında büyük sorunlara neden olur. Ekip bunu düşünmüş ve Clientlarda gurup özelliğini eklemiş. Yani gelen client’ı bir guruba ekleyip, sadece bir guruba push edebilirsin. Örnek kod aşağıdaki gibidir.

      İyi çalışmalar.

      public override Task OnConnected()
      {
      Groups.Add(Context.ConnectionId, “Group1”);
      JoinRoom(“Group1″);
      }

      public async Task JoinRoom(string groupName)
      {
      Clients.Group(groupName).addChatMessage(groupName+”gurubuna yeni bir kişi katılmıştır.”);
      }

  4. Fatih diyor ki:

    Hocam merhaba :) Yazınız güzel elinize sağlık ilk araştırmamı makaleniz üzerinden yaptım. Amma velakin :) bu kadarlık bir kullanım artık işimi görmüyor :( Bunun için bir kaç sorum olacak :)

    1-SignalR Client Id yi her zaman “connection.hub.start()” olduğunda mı üretir ve her durumda sayfa refresh olmadıkça yeni bir clientId üretmez mi?(callback/ajax request kullandığım yerler için)

    2-Aşağıdaki bağlantıda tarayıcılara göre max limitleerin olduğu bir tablo bulunmaktadır.
    http://www.browserscope.org/?category=network&v=top
    Örneğin chrome kullanırken bazen “websocket bekleniyor” gibi bir yazı geliyor ve sayfa açılmıyor. Acaba kullanılmayan bağlantıları sonlandırmadığımız için mi böyle bir sorun çıkıyor? sonlandırmak için ne yapmalıyız?

    myHubProxy.client.stopClient = function() {
    $.connection.hub.stop();
    };
    Gibi bir yol mu izlemeliyiz? Ne zaman kullanılmayan(sayfadan ayrılan) bağlantıları nasıl sonlandırabiliriz.

    Teşekkürler :)

    • borsoft diyor ki:

      Selam Fatih,
      Öncelikle teşekkürler.

      1-)ClientID üretimi connection.hub.start()’anında olur. Yeni bir client Connect olurken ona özel ID yaratılır. Aynı Sql’de yeni bir kayıt yaratırken @@identity ile yeni oluşan ID almak olarak düşünebilirsin. Refresh olmadan şu an için üretilemiyor diye biliyorum. Ama product guruba bunun ile ilgili bir geliştirme var mı diye sorucam…

      2-)Sayfadan ayrılan yani session’ı düşen client’ın connection’ı hemen kaybolur. Senin ayrıca düşürmene gerek yok. Ama düşürmen gerekirse yazdığın kodlar evet geçerli..Refresh zamanında bile disconnect işlemi olur.
      Hatta düşüp düşmediğini alttaki kod ile yakalayabilirsin.

      İyi çalışmalar.

      public override System.Threading.Tasks.Task OnDisconnected(bool stopCalled)
      {
      if (stopCalled)
      {
      Console.WriteLine(String.Format("Client {0} explicitly closed the connection.", Context.ConnectionId));
      }
      else
      {
      Console.WriteLine(String.Format("Client {0} timed out .", Context.ConnectionId));
      }

      return base.OnDisconnected();
      }

  5. Gökhan diyor ki:

    Hocam elinize sağlık çok güzel anlatım olmuş. Ancak rica etsem asp.net için c# anlatabilir misiniz?
    Mvc kullanmıyorum henüz, projemde çok lazım bu sistem.

    • borsoft diyor ki:

      Selam Gökhan,

      Öncelikle teşekkürler. Gerçek hayat için MVC öğrenmeni tavsiye ederim. 2. olarak farklı konularda yazdığım için bu konuyu bir daha yazmaya malesef vaktim yok. Ama sen Asp.Net’de MVC’den yola çıkarak kendin yazmaya çalış. Takıldığın yerleri de Mail yolu ile bana sor.

      İyi çalışmalar.

  6. Cihan diyor ki:

    süper bir makale teşekkürler

  7. Umut diyor ki:

    Hocam Merhabalar..
    Güzel bir makale olmuş, bu yüzden bir teşekkür de ben eklemek istiyorum.

    ASP.NET ile hazırlanmış bir e-ticaret sitesi için Native Android uygulaması hazırlıyorum. Uygulamanın bölümlerini Realtime olarak yapmaya niyetlendim ve bu sebepten ötürü SignalR ile tanıştım.

    Bu konuyla ilgili Türkçe kaynak maalesef yok denecek kadar az.(Hatta SignalR’ın Android ile kullanımı konusunda yabancı kaynak bulmakta pek kolay olmadı) Bu noktada makaleniz ilaç gibi geldi diyebilirim :)
    Sonuç olarak istediğimi yaptım ve mutluyum.

    Araştırma yaparken kafama takılan bir kaç soru oldu. Bu konuda yardımcı olursanız daha da mutlu olacağım. :)

    Sorular :
    1-) Android Client tarafında Server Sent Events kullanmaktayım. Bunun yerine Long Polling kullanma özgürlüğüne de sahibim. Fakat böyle bir seçim yapmanın bana performans açısından bir avantajı yada dezantajı olur mu? (Yada hiç dokunmayıp böyle devam mı edeyim?)

    2-) Genel olarak bakıldığında Geliştiriciler çoğunlukla Restful Servisi kullanmakta.. Merak ettiğim ise neden Geliştiriciler SignalR yerine Web Api tercih etmekteler? Restful Api’lerin performansı SignalR’a göre çok daha mı iyi? Yoksa bu durumun tek nedeni kaynak yetersizliği yada SignalR’ın biraz daha uğraştırıcı olması mı?
    (Çünkü sürekli GET-POST işlemi yapmak yerine Real Time veri akışı uygulamayı daha hızlı hale getirir diye düşünüyorum.)

    3-) Yoğun veri alış-verişinin olduğu bir uygulamada SignalR kullanımının dezavantajları var mıdır? Herhangi bir veri bozulması durumu söz konusu olur mu? (İnternette dolanırken böyle bir soruya denk gelmiştim de cevabına rastlamamıştım)

    İyi çalışmalar..

    • borsoft diyor ki:

      Selam Umut,

      Öncelikle güzel yorumların için teşekkürler. Makalemin işine yaramasına çok sevindim.

      1-)Long Polling performans kaybına kesin yol açar :)

      2-)SignalR ve WebApi Restfull birbirinden çok farklı konular. SignalR serverdan clientlara bir push işlemi. Restfull ise client’dan server’a cross domain’e düşmeden get post işlemi. Eğer canlı data ve bunun gösterimi senin için hayati bir önem taşıyorsa mesela monitöring ekranları yapıyorsan tabiki socket teknolojisi kullanacaksın. Ciddi bir performans harcar. Yok hayır ben timer ile her 1 dakikada bir client tarafdan ilgili servise request çekerim ve gelen dataya göre client’daki datayı güncellerim dersen WebApi restFull. Tabiki signalR’a göre ciddi bir performans KAZANCI ve çok az kaynak tüketimi olur. Ama bu örnekde olduğu gibi en yakın datayı 1 dakika geriden takip edersin. Umarım biraz olsun aydınlatabilmişimdir.

      3-) Yoğun tarifikte signalR ile veri kaybı yaşamaz ama ciddi bir tırafik alacağın için Redis veya Azure üzerinde scale yani 4-5 sunucu üzerinde signalR paralel programing yapman gerekir.

      İyi çalışmalar. Hoşçakal.

Bir Cevap Yazın

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