SignalR ile Canlı Kullanıcı Sayısını Bulma ve Redis ile Loglama Bölüm 1

Selamlar,

Bugün farklı sunuculardaki bir portalın kullanıcı sayılarını, sunucu bazında real time olarak bulup hem bir monitör ekranında göstericeğiz, hem de redis ‘in pub/sub özelliğini kullanıp MsSql bir db’ye logluyacağız.

Öncelikle online kullanıcı sayısının alınacağı sayfayı Mvc ile oluşturalım.

index.cshtml: Aşağıdaki görüldüğü gibi online kullanıcı sayısının bulanacağı borakasmer.com’un örnek bir demo sayfası hazırlanmıştır. Css için Nuget’den bootstarp indirilmiştir.

Örnek kullanıcı sayısının bulunacağı sayfa.

BoraBlog

Öncelikle online kullanıcı sayısının sayılabilmesi için Nuget’den aşağıdaki SignalR paketi indirilir. Bu makale yazılırken ki en güncel stabil sürüm 2.2.0 dır.

NugetPacket

SignalR “Product” hub sınıfı aşağıda görüldüğü gibi oluşturulur. Daha sonra bu sınıfa ait “OnConnected()” ve “OnDisconnected()” methodları override edilecektir. Method isimlerinden de anlaşılacağı gibi, client sayfaya ilk geldiğinde ve ayrılındığında sayım amaçlı birtakım işlemler yapılacaktır. SignalR hakkında detaylı bilgiliyi önceki makalemden okuyabilirsiniz.

Öncelikle client side tarafında signalR sınıfına nasıl connect olunuyor onu inceleyelim. Aşağıda görüldüğü gibi ilgili signalR.js ve magic script dediğimiz “~/signalr/hubs” aşağıdaki gibi sayfaya eklenmiştir. İlgili connection işlemi yapılıp console’a ilgili bildiri yazılmıştır. Burada önemli nokta aşağıda gördüğünüz gibi “sayHello()” function’ı dır. Normalde hiçbir işe yaramamaktadır. Yalnız “hubProxy“‘e herhangi bir function bağlanmadan, server side taraftaki “OnConnected()” methodu tetiklenmemektedir. Console’a ilgili mesaj yazılsa da server side tarafa, ilgili benzer function(“sayHello()“) yazılmadan düşülememektedir. Bu sanırım ileride düzeltilecek bir durum:)

Sıra geldi ilgili canlı kullanıcı sayısını saymaya. Öncelikle aşağıda görüldüğü gibi “UserClass” adında bir static sınıfı oluşturulmuştur. “users” List : Uniq bir ID ile gelicek olan userları saklarken, Lock nesnesi olarak, “lockObject” kullanılacaktır.

OnConnected()” methodu’na aşağıdaki kodlar eklenir: Öncelikle, gelen client’ın “ConnectionId“‘si alınmıştır. Ilgili “clientID“‘ye göre böyle bir user’ın “users” Liste’de olup olmadığına bakılır. Eğer yok ise ilgili client ‘ın girdiği sayfaya bakılır. Örneğin aşağıda client’ın girdiği sayfanın “Admin” olup olmadığına bakılmıştır. Admin değil ise ve önceden bu user listede yok ise, ilgili user listeye eklenir. Aşağıda yorumlanan bir satır vardır. İstenir ise ilgili client bir guruba’da örneğin aşağıda “index” gurubuna da eklenebilirdi. Buradan çıkarılacak ders, amaca göre girilen sayfa guruplarına göre de kullanıcı sayası tutulabilinir. Bu örnekde “Admin” sayfasında ve server bazında toplam kullanıcı sayısı gösterileceği için sadece “index” sayfasına giren userların sayısı saydırılmıştır.

HomeController.cs/Product:Hub :

Client çıkış yaptığı zaman aşağıdaki “OnDisconneted()” methodu tetiklenmektedir.  Burada farklı olan durum, client’ın geldiği sayfanın “Headers” bilgisinin okunamamasıdır. Çünkü sayfa çoktan kapanmıştır:) Kapanmış bir sayfanın header’ının okunamaması dan dolayı, ilgili kodlar yorumlanmıştır. Eğer ilgili client, yani az önce sayfaydan çıkan user,  listede var ise çıkarılmaktadır.

Not: Eğer birkaç tane guruba göre kullanıcı sayısı tutuluyor ise, o zaman client’ın her bir guruba göre tek tek bakılıp çıkarılmasına gerek yoktur. Zaten asenkron olarak yapılmaya çalışılan bu işlem hiçbir zaman tamamlanamayacaktır. Çünkü client ile signalR arasında, hiçbir connection kalmamıştır. .Net disconnect durumunda kendi Groups sınıfını optimize edecektir. Ayrıca bir işlem yapılmasına gerek yoktur.

Şimdi sıra geldi ilgili kullanıcı sayısını bir de cache’de tutup, cache time out olduğu zaman server bazında MsSql bir DB’ye kaydetmeye. Ben bunun için Redis kullandım. Eğer redis hakkında daha detaylı bir bilgi edinmek isterseniz önceki makalemi inceleye bilirsiniz. Windows 10 ortamında redis’in çalışması için ilgili “redis-server.exe” ve “redis-cli.exe” dosyaları indirilir. Windows 10 ortamında redis server çalıştırılınca aşağıdaki gibi bir console ekranı ile karşılaşılır. Artık redis Windows ortamında ayağa kaldırılmıştır.

RedisClient

HomeController.cs/Product:Hub(Full): İlgili Product sınıfı aşağıdaki gibi değiştirilir. Öncelikle “RedisEndpoint()” default port olan:6379’dan local’e bağlanılır. Ayrıca web.config’den “ServerName”‘i alınır. Amaç hangi sunucudaki client sayısının toplanacağının belirlenmesidir.

Web.config: “ServerName”‘in tanımlandığı satır.

using()” içerisinde ilgili “RedisCliemt()” tanımlanan EndPoint’e göre oluşturulur. Ve “OnConnected()” işleminin sonunda “CheckAndSetUserCount()” methodu çağrılır. İlgili method asenkron olarak çağrılmaktadır. Aynı işlemler “OnDisconnected()” methodu için de tekrarlanır.

CheckAndSetUserCount()” methodu 4 parametre beklemektedir. İlk parametre RedisClient’dır. “Web.config”‘den alınan connection’a göre, bağlanılan redis server nesnesi parametre olarak verilir. 2. parametre “userCount” yani toplam kullanıcı sayısının tutulduğu static users adlı listedeki eleman sayısı toplamıdır. 3. Parametre client’ın connect olup olmadığının bilgisidir. Son yani 4. parametre client’ın bağlandığı sayfanın “Admin” sayfası olup olmadığına bakılır. “userCountWithServer” değişkeni adındanda anlaşılacağı gibi, “Web.config”‘den çekilen server ismi ve toplam online client sayısı birleştirilerek oluşturulan string bir değişkendir. Kısaca server’a ait toplam kullanıcı sayısını vermektedir.

Yukarıdaki örnekde görüldüğü gibi:

  • Gelen client “Admin” ekranınden gelmiş ise “Groups.Add(Context.ConnectionId, “admin”)” ile geldiği connectionID ile birlikte “admin”  gurubuna eklenir. Amaç online kullanıcı bilgisinin sadece bu guruba dahil clientlarda gösterilmesini sağlamaktır.
  • Redis Cache’e ya ilkkez geliniyor ise ya da time out’a düşülmüş ise  yandaki koşul sağlanmış olunur. “client.ContainsKey(“UserCount”)” Amaç redis cache’in timeout durumunda, toplam kullanıcı sayısının ayrıca yazılmış bir console application’a gönderilmesidir.(Pub/sub)
  • Normalde Redis Cache’e deger atanırken lock işlemine gerek yoktur. Ama burada ayrıca “PublishMessage()” methodu da çağrıldığı için static “UserClass.lockObject” lock nesnesi kullanılmıştır. Aynı anda cache timeout’a düşen 1000 kullanıcının bu methodu topluca çağırmasını engellemek için, ilk gelen client’ın bu işlemi gerçekleştirirken diğer clientların bu işlemi beklemesi, ilgili static obje ile sağlanmaktadır.
  • Redis Cache’e, “TimeSpan” ile 20sn lik expire süresi atanmıştır.  TimeOut durumunda, var olan toplam yeni kullanıcı sayısı tekrardan ilgili cache’e atanacakdır.
  • Redis Pub/Sub işleminde,  yandaki method ile “client.PublishMessage(“UserCountService”, userCountWithServer)” oluşturulacak bir console application’a, kullanıcı sayısı ve server ismi ile birlikte gönderilir.
  • Son olarak ilgili “admin” gurubunun tamamında client side’da “refreshUserCount” function’ı, ilgili kullanıcı sayısı ve server ismi ile birlikte tetiklenir. Böylece yeni kullanıcı girişinde ve çıkışında, değişen toplam kullanıcı sayısı monitoring ekranında yenilenmiş olunur.

Admin.cshtm: Aşağıda görüldüğü gibi öncelikle ilgili signalR Hub sınıfına bağlanılmış ve “refreshUserCount()” function’ı tanımlanmıştır. İlgili functionda gelen data split ile “server ismi” ve “toplam kullanıcı” sayısı olarak ayrılmıştır. Daha sonra gelen datadan server ismi ile “id” değeri aynı olan bir “<td>” içine online kullanıcı sayısı basılır. Böylece Admin ekranında kullanıcı sayısı SignalR ile real time olarak gösterilmiş olunur.

Admin2

Geldik makelenin ilk bölümünün sonuna. Bu bölümde bir index sayfası için var olan kullanıcı sayısını real time olarak bulduk ve bunu Admin bir sayfada yine real time olarak SignalR socket kullanarak gösterdik. Ayrıca loglama amacı ile redis cache kullanıp, timeout zamanında bir sonraki bölümde yazılacak console application’a, kullanıcı sayısını sunucu bilgisi ile birlikte gönderdik. Bir sonraki bölümde bu bilgilerin gönderildiği consol application’ı yazıp MsSql server’a kaydedeceğiz. Ayrıca farklı bir sunucuda çalışan benzer bir Index sayfası yapıp, farklı sunuculardaki farklı online kullanıcı sayılarını ve toplamını gösteren bir Glabal Monitor sayfası oluşturacağız. Burada üzerinde düşünülmesi gereken ve maklenin esas amacı olan kısım, tüm sunuculardaki toplam kullanıcı sayısının nasıl gösterileceğidir. Herbir sunucuya ait signalR sınıfı ve kullanıcı sayısı farklı iken hepsini tek bir çatıda toplamak performans açısından çok hasas bir konudur.

Bir sonraki makalede görüşmek üzere hoşçakalın.

Herkes Görsün:

Bunlar da hoşunuza gidebilir...

2 Cevaplar

  1. Ahmet dedi ki:

    Bora abi bu mükemmel örnek için teşekkürler, yine muhteşem bir makale olmuş. Üyeleri ve ziyaretçileri birbirinden ayırmak için session ile oturum aşmış kullanıcıların sessionId değerlerini signalr ile nasıl alabiliriz? signalr ile o kullanıcı ile iletişime geçmek istiyorum. Mesela oturum aşmış üyelerden birine anlık mesaj göndermek için kullanmak isteyebilirim.

  2. Romario dedi ki:

    Redis’in kullanımına örnek ve SignalR bilgisini gözden geçirmek açısından oldukça iyi bir makale.

Bir Cevap Yazın

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